android-XML解析之pull类型代码解析

    由于之前的项目使用过xml文件的解析,但是在使用的时候都是从网上找到的代码,稍作修改就使用了,然而对其中的原理并不知所以然,所以再次想使用的时候,感觉还是空空的,不知道如何下手,为了更加深入的理解xml文件的解析,我从代码实现的角度做一些理解,但是我在看代码的时候,有些代码的实现还是没有办法读懂,还是太深奥。下面我就以我之见,写一些东西,作为记录。

    在android中XML的解析有三种,分别为:SAX(Simple API XML)、DOM(document object model)、以及今天我们要说的PULL类型,也是android官方提倡的使用方式。

    在讲之前,我先写出一般的使用方式,比如解析以下的XML文档:


<?xml version="1.0" encoding="UTF-8"?>
<students>
   <student id="1">
      <name>cc</name>
      <age>10</age>
      <grade>100</grade>
   </student>
   <student id="2">
      <name>cy</name>
      <age>11</age>
      <grade>89</grade>
   <student>
</students>

一般的解析手段,java逻辑代码如下(代码截取与网络):

public class TestPullXml {
    public List<Person> getPersons(InputStream instream) throws Exception {
        List<Person> persons = null;
        Person person = null;
        XmlPullParser parser = Xml.newPullParser();//得到Pull解析器
        parser.setInput(instream, "UTF-8");//设置下输入流的编码
        int eventType = parser.getEventType();//得到第一个事件类型
        while (eventType != XmlPullParser.END_DOCUMENT) {
            //如果事件类型不是文档结束的话则不断处理事件
            switch (eventType) {
                case XmlPullParser.START_DOCUMENT://如果是文档开始事件
                    persons = new ArrayList<Person>();创建一个person集合
                    break;
                case (XmlPullParser.START_TAG)://如果遇到标签开始
                    String tagName = parser.getName();// 获得解析器当前元素的名称
                    if ("person".equals(tagName)) {//如果当前标签名称是   
                        <person> person = new Person();//创建一个person
                        //将元素的属性值赋值给id
                        person.setId(new Integer(parser.getAttributeValue(0)));
                    }
                    if (person != null) {//如果person已经创建完成
                        if ("name".equals(tagName))//如果当前节点标记是name
                            person.setName(new String(parser.nextText()));
                        else if ("age".equals(tagName))//如果当前元素节点标记是age
                            person.setAge(new Short(parser.nextText()));
                    }
                    break;
                case (XmlPullParser.END_TAG)://如果遇到标签结束
                    if ("person".equals(parser.getName())) {//如果是person标签结束
                        persons.add(person);//将创建完成的person加入集合
                        person = null;//并且置空
                    }
                    break;
            }
            eventType=parser.next();//进入下一个事件处理
        }
        return persons;
    }

下面,我们对上述代码,进行分行理解:


XmlPullParser parser = Xml.newPullParser();//得到Pull解析器
首先找到Xml类下的newPullParser方法:



/**
  * Returns a new pull parser with namespace support.
  */
    public static XmlPullParser newPullParser() {
        try {
            KXmlParser parser = new KXmlParser();
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
            return parser;
        } catch (XmlPullParserException e) {
            throw new AssertionError();
        }
    }

通过上面的代码,我们可以发现,实际上该函数返回了一个实现了XmlPullParser接口的类KXmlParser,并且给该对象设置了命名空间和文档声明,所以,对以后的parser对象,我们所需要分析的实现代码,就要找KXmlParser这个类了。

parser.setInput(instream, "UTF-8");//设置下输入流的编码
这一步,我们先查看一下,官方文档是怎么解释了:


Set the input source for parser to the given reader and resets the parser. The event type is set to the initial value START_DOCUMENT. Setting the reader to null will just stop parsing and reset parser state, allowing the parser to free internal resources such as parsing buffers.



相信大家可以理解这个方法的作用,但是我们还是看看它的实现代码吧:


 //  public part starts here...

    public void setInput(Reader reader) throws XmlPullParserException {
        this.reader = reader;
        line = 1;
        column = 0;
        type = START_DOCUMENT;
        name = null;
        namespace = null;
        degenerated = false;
        attributeCount = -1;
        encoding = null;
        version = null;
        standalone = null;

        if (reader == null)
            return;

        srcPos = 0;
        srcCount = 0;
        peekCount = 0;
        depth = 0;

        entityMap = new Hashtable<String, String>();
        entityMap.put("amp", "&");
        entityMap.put("apos", "'");
        entityMap.put("gt", ">");
        entityMap.put("lt", "<");
        entityMap.put("quot", "\"");
    }

在这个方法中,初始化了很多在读取XML过程中需要使用的成员变量,比如type,初始化为START_DOCUMENT,输入流instream,这些都是解析时所必不可少的成员,所以在一开始,一定要调用该方法。设置输入源。


int eventType = parser.getEventType();//得到第一个事件类型



首先我们要知道,XML的几种事件类型:


START_DOCUMENT、START_TAG、TEXT、END_TAG、END_DOCUMENT

 然后,我们看下面的一句代码:

int eventType = parser.getEventType();//得到第一个事件类型
其实,这句我们很明显就知道,返回一定是START_DOCUMENT,因为在上一句,setInput()函数中,初始化了type类型为START_DOCUMENT。

在下面的while循环中就是根据这五种事件类型不同的解析数据,很简单。这里不多说了。

下面有一句:

 eventType=parser.next();//进入下一个事件处理
也就是这个next函数。

看看官方的文档:

Get next parsing event - element content will be coalesced and only one TEXT event
must be returned for whole element content (comments and processing instructions 
will be ignored and emtity references must be expanded or exception mus be thrown 
if entity reerence can not be exapnded). If element content is empty (content is ""
) then no TEXT event will be reported.



翻译:"得到下一个解析事件,元素的内容将会被合并并且一个TEXT事件肯定会返回整个事件的内容"







你可能感兴趣的:(android-XML解析之pull类型代码解析)