ddu-xml

一、DOM解析XML

我们首先来看看DOM(Document Object Model)这种方式解析xml,通过DOM解析xml在j2ee开发中非常的常见,它将整个xml看成是一个树状的结构,在解析的时候,会将整个xml文件加载到我们的内存当中,然后通过DOM提供的API来对我们的xml数据进行解析,这种方式解析xml非常的方便,并且我们可以通过某个节点访问到其兄弟或者是父类、子类节点。那么通过DOM来解析xml的步骤是怎样的呢?

1.首先通过DocumentBuilderFactory这个类来构建一个解析工厂类,通过newInstance()的方法可以得到一个DocumentBuilderFactory的对象。

2.通过上面的这个工厂类创建一个DocumentBuilder的对象,这个类就是用来对我们的xml文档进行解析,通过DocumentBuilderFactory的newDocumentBuilder()方法

3.通过创建好的 DocumentBuilder 对象的 parse(InputStream) 方法就可以解析我们的xml文档,然后返回的是一个Document的对象,这个Document对象代表的就是我们的整个xml文档。

4.得到了整个xml的Document对象后,我们可以获得其下面的各个元素节点(Element),同样每个元素节点可能又有多个属性(Attribute),根据每个元素节点我们又可以遍历该元素节点下面的子节点等等。

在这里要说明一下,在DOM的API当中,Node这个接口代表了我们整个的DOM对象的最初数据类型,它代表了整个document树中的每一个单一节点。所有实现了Node这个接口的对象都可以处理其孩子节点,当然,并不是每个节点都有children,例如TextNode(文本节点),通过Node的 nodeName、nodeValue、attributes这三个属性,我们可以很方便的得到每个Node节点的节点名字、节点的值、节点属性等,下面我们来看看不同类型的Node节点其nodeName、nodeValue、attributes三个属性

首先我们构建一个xml的文档,这个文档等下会放在我们的服务器上,通过http协议来得到这个xml文档,然后在我们的Android客户端对其进行解析

<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="1">
<name>张三</name>
<age>15</age>
</person>
<person id="2">
<name>李四</name>
<age>16</age>
</person>
<person id="3">
<name>李四</name>
<age>17</age>
</person>
</persons>

下面我们来看看DOM解析服务器端xml的工具类:

public class DomParserUtils
{
public static List<Person> parserXmlByDom(InputStream inputStream) throws Exception
{
List<Person> persons = new ArrayList<Person>();
// 得到一个DocumentBuilderFactory解析工厂类
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 得到一个DocumentBuilder解析类
DocumentBuilder builder = factory.newDocumentBuilder();
// 接收一个xml的字符串来解析xml,Document代表整个xml文档
Document document = builder.parse(inputStream);
// 得到xml文档的根元素节点
Element personsElement = document.getDocumentElement();
// 得到标签为person的Node对象的集合NodeList
NodeList nodeList = personsElement.getElementsByTagName("person");
for(int i = 0; i < nodeList.getLength(); i++)
{
Person person = new Person();
// 如果该Node是一个Element
if(nodeList.item(i).getNodeType() == Document.ELEMENT_NODE)
{
Element personElement = (Element)nodeList.item(i);
// 得到id的属性值
String id = personElement.getAttribute("id");
person.setId(Integer.parseInt(id));

            //    得到person元素下的子元素
            NodeList childNodesList = personElement.getChildNodes();
            for(int j = 0; j < childNodesList.getLength(); j++)
            {
                if(childNodesList.item(j).getNodeType() == Document.ELEMENT_NODE)
                {
                    //    解析到了person下面的name标签
                    if("name".equals(childNodesList.item(j).getNodeName()))
                    {
                        //    得到name标签的文本值
                        String name = childNodesList.item(j).getFirstChild().getNodeValue();
                        person.setName(name);
                    }
                    else if("address".equals(childNodesList.item(j).getNodeName()))
                    {
                        String age = childNodesList.item(j).getFirstChild().getNodeValue();
                        person.setAge(Integer.parseInt(age));
                    }
                }
            }
            
            persons.add(person);
            person = null;
        }
    }
    return persons;
}

}

通过DOM解析xml的好处就是,我们可以随时访问到某个节点的相邻节点,并且对xml文档的插入也非常的方便,不好的地方就是,其会将整个xml文档加载到内存中,这样会大大的占用我们的内存资源,对于手机来说,内存资源是非常非常宝贵的,所以在手机当中,通过DOM这种方式来解析xml是用的比较少的。

二、SAX解析XML

SAX(Simple API for XML),接着我们来看看另一种解析xml的方式,通过sax来对xml文档进行解析。

SAX是一个解析速度快并且占用内存少的xml解析器,非常适合用于Android等移动设备。 SAX解析XML文件采用的是事件驱动,也就是说,它并不需要解析完整个文档,在按内容顺序解析文档的过程中,SAX会判断当前读到的字符是否合法XML语法中的某部分,如果符合就会触发事件。所谓事件,其实就是一些回调(callback)方法,这些方法(事件)定义在ContentHandler接口。下面是一些ContentHandler接口常用的方法:

startDocument()
当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。

endDocument()
和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。

startElement(String namespaceURI, String localName, String qName, Attributes atts)
当读到一个开始标签的时候,会触发这个方法。namespaceURI就是命名空间,localName是不带命名空间前缀的标签名,qName是带命名空间前缀的标签名。通过atts可以得到所有的属性名和相应的值。要注意的是SAX中一个重要的特点就是它的流式处理,当遇到一个标签的时候,它并不会纪录下以前所碰到的标签,也就是说,在startElement()方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元属等等其它与结构相关的信息,都是不得而知的,都需要你的程序来完成。这使得SAX在编程处理上没有DOM来得那么方便。

endElement(String uri, String localName, String name)
这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。

characters(char[] ch, int start, int length)
这个方法用来处理在XML文件中读到的内容,第一个参数用于存放文件的内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。

上面提到了重要的一点,sax解析xml是基于事件流的处理方式的,因此每解析到一个标签,它并不会记录这个标签之前的信息,而我们只会知道当前这个表情的名字和它的属性,至于标签里面的嵌套,上层标签的名字这些都是无法知道的。

sax解析xml最重要的步骤就是定义一个我们自己的Handler处理类,我们可以让其继承 DefaultHandler 这个类,然后在里面重写其回调方法,在这些回调方法里来做我们的xml解析

下面我们就通过一个实例来看看如果通过SAX来解析xml,首先定义一个我们自己的Handler类:

public class MyHandler extends DefaultHandler
{
private List<Person> persons;
private Person person;
// 存放当前解析到的标签名字
private String currentTag;
// 存放当前解析到的标签的文本值
private String currentValue;

public List<Person> getPersons()
{
    return persons;
}

//    当解析到文档开始时的回调方法
@Override
public void startDocument() throws SAXException
{
    persons = new ArrayList<Person>();
}

//    当解析到xml的标签时的回调方法
@Override
public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException
{
    if("person".equals(qName))
    {
        person = new Person();
        //    得到当前元素的属性值
        for(int i = 0; i < attributes.getLength(); i++)
        {
            if("id".equals(attributes.getQName(i)))
            {
                person.setId(Integer.parseInt(attributes.getValue(i)));
            }
        }
    }
    //    设置当前的标签名
    currentTag = qName;
}

//    当解析到xml的文本内容时的回调方法
@Override
public void characters(char[] ch, int start, int length)
        throws SAXException
{
    //    得到当前的文本内容
    currentValue = new String(ch,start, length);
    //    当currentValue不为null、""以及换行时
    if(currentValue != null && !"".equals(currentValue) && !"\n".equals(currentValue))
    {
        //    判断当前的currentTag是哪个标签
        if("name".equals(currentTag))
        {
            person.setName(currentValue);
        }
        else if("age".equals(currentTag))
        {
            person.setAge(Integer.parseInt(currentValue));
        }
    }
    //    清空currentTag和currentValue
    currentTag = null;
    currentValue = null;
}

//    当解析到标签的结束时的回调方法
@Override
public void endElement(String uri, String localName, String qName)
        throws SAXException
{
    if("person".equals(qName))
    {
        persons.add(person);
        person = null;
    }
}

}

接着看看SAX解析xml的Util类:

复制代码
public class SaxParserUtils
{
public static List<Person> parserXmlBySax(InputStream inputStream) throws Exception
{
// 创建一个SAXParserFactory解析工厂类
SAXParserFactory factory = SAXParserFactory.newInstance();
// 实例化一个SAXParser解析类
SAXParser parser = factory.newSAXParser();
// 实例化我们的MyHandler类
MyHandler myHandler = new MyHandler();
// 根据我们自定义的Handler来解析xml文档
parser.parse(inputStream, myHandler);

    return myHandler.getPersons();
}

}

三、PULL解析XML

最后来介绍第三种解析xml的方式,pull。pull解析和sax解析类似,都是基于事件流的方式,在Android中自带了pull解析的jar包,所以我们不需要导入第三方的jar包了。

Pull解析器和SAX解析器的区别:

Pull解析器和SAX解析器虽有区别但也有相似性。他们的区别为:SAX解析器的工作方式是自动将事件推入注册的事件处理器进行处理,因此你不能控制事件的处理主动结束;

而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。这是他们主要的区别。

而他们的相似性在运行方式上,Pull解析器也提供了类似SAX的事件(开始文档START_DOCUMENT和结束文档END_DOCUMENT,开始元素START_TAG和结束元素END_TAG,遇到元素内容TEXT等),但需要调用next() 方法提取它们(主动提取事件)。

Android系统中和Pull方式相关的包为org.xmlpull.v1,在这个包中提供了Pull解析器的工厂类XmlPullParserFactory和Pull解析器XmlPullParser,XmlPullParserFactory实例调用newPullParser方法创建XmlPullParser解析器实例,接着XmlPullParser实例就可以调用getEventType()和next()等方法依次主动提取事件,并根据提取的事件类型进行相应的逻辑处理。

下面我们就来通过一个代码来看看pull解析xml的步骤:

public class PullParserUtils
{
public static List<Person> parserXmlByPull(InputStream inputStream) throws Exception
{
List<Person> persons = null;
Person person = null;

    //    创建XmlPullParserFactory解析工厂
    XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
    //    通过XmlPullParserFactory工厂类实例化一个XmlPullParser解析类
    XmlPullParser parser = factory.newPullParser();
    //    根据指定的编码来解析xml文档
    parser.setInput(inputStream, "utf-8");
    
    //    得到当前的事件类型
    int eventType = parser.getEventType();
    //    只要没有解析到xml的文档结束,就一直解析
    while(eventType != XmlPullParser.END_DOCUMENT)
    {
        switch (eventType)
        {
            //    解析到文档开始的时候
            case XmlPullParser.START_DOCUMENT:
                 persons = new ArrayList<Person>();
            break;
            //    解析到xml标签的时候
            case XmlPullParser.START_TAG:
                 if("person".equals(parser.getName()))
                 {
                     person = new Person();
                     //    得到person元素的第一个属性,也就是ID
                     person.setId(Integer.parseInt(parser.getAttributeValue(0)));
                 }
                 else if("name".equals(parser.getName()))
                 {
                     //    如果是name元素,则通过nextText()方法得到元素的值
                     person.setName(parser.nextText());
                 }
                 else if("age".equals(parser.getName()))
                 {
                     person.setAge(Integer.parseInt(parser.nextText()));
                 }
            break;
            //    解析到xml标签结束的时候
            case XmlPullParser.END_TAG:
                 if("person".equals(parser.getName()))
                 {
                     persons.add(person);
                     person = null;
                 }
            break;
        }
        //    通过next()方法触发下一个事件
        eventType = parser.next();
    }
    
    return persons;
}

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容