android-----三种方式(sax、pull、dom)解析xml文件

来源:互联网 发布:个性淘宝店铺女装店名 编辑:程序博客网 时间:2024/06/05 18:49

解析xml文件常用的有三种方法,分别是sax、pull、dom。

现在分别用这三种方式解析一个xml文件,看看有什么区别。

准备工作如下:

新建一个android工程,按如下的目录结构创建相应的文件

weather.xml文件的代码如下:

<?xml version="1.0" encoding="utf-8"?><infos><city id="1"><temp>24'C/32'C</temp><weather>多云</weather><wind>南风3级</wind><name>苏州</name><pm>66</pm></city><city id="2"><temp>26'C/32'C</temp><weather>小雨</weather><wind>东风2级</wind><name>广州</name><pm>121</pm></city><city id="3"><temp>18'C/23'C</temp><weather>晴天</weather><wind>北风2级</wind><name>北京</name><pm>180</pm></city></infos>


布局文件的代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context=".MainActivity" >    <Button        android:id="@+id/button1"        android:layout_width="wrap_content"        android:layout_height="wrap_content"       android:layout_gravity="center_horizontal"        android:onClick="sax"        android:text="SAX" />    <Button        android:id="@+id/button2"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center_horizontal"        android:onClick="pull"        android:text="Pull" />    <Button        android:id="@+id/button3"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center_horizontal"        android:onClick="dom"        android:text="Dom" />    <TextView        android:id="@+id/textView1"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="showdata" /></LinearLayout>

WeatherInfo.java文件代码:

package com.myself.parsexml.entity;public class WeatherInfo {private int id;private String temp;private String weather;private String wind;private String name;private String pm;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getTemp() {return temp;}public void setTemp(String temp) {this.temp = temp;}public String getWeather() {return weather;}public void setWeather(String weather) {this.weather = weather;}public String getWind() {return wind;}public void setWind(String wind) {this.wind = wind;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPm() {return pm;}public void setPm(String pm) {this.pm = pm;}@Overridepublic String toString() {return "[id=" + id + ", 温度=" + temp + ", 天气=" + weather + ", 风力="+ wind + ", 城市=" + name + ", pm2.5=" + pm + "]";}}


 

第一种方式:sax方式解析xml文件

SAX解析XML文档采用事件驱动模式。什么是事件驱动模式?它将XML文档转换成一系列的事件,由单独的事件处理器来决定如何处理。

基于事件驱动的处理模式主要是基于事件源和事件处理器(或者叫监听器)来工作的。一个可以产生事件的对象叫做事件源,而一个可以针对事件做出响应的对象就被叫做事件处理器。

 

MainActivity.java文件中的主要代码:

public void sax(View view) {try {ArrayList<WeatherInfo> infos = WeatherService.getWeatherInfoBySax(MainActivity.class.getClassLoader().getResourceAsStream("weather.xml"));//将xml文件转换为输入流StringBuffer sb = new StringBuffer();for (WeatherInfo info : infos) {sb.append(info);sb.append("\n");}tv_info.setText("sax方式解析xml文件:\n"+sb.toString());} catch (Exception e) {e.printStackTrace();Toast.makeText(this, "sax方式解析xml文件失败", 0).show();}}


 WeatherService.java文件中的代码:

public static ArrayList<WeatherInfo> getWeatherInfoBySax(InputStream is)throws Exception {// 实例化一个SAXParserFactory对象SAXParserFactory factory = SAXParserFactory.newInstance();SAXParser parser;// 实例化SAXParser对象,创建XMLReader对象,解析器parser = factory.newSAXParser();XMLReader xmlReader = parser.getXMLReader();// 实例化handler,事件处理器SAXPraserHelper helperHandler = new SAXPraserHelper();// 解析器注册事件xmlReader.setContentHandler(helperHandler);// 读取文件流InputStream stream = is;InputSource source = new InputSource(stream);// 解析文件xmlReader.parse(source);return helperHandler.getList();}

SAX解析方式最重要的是ContentHandler接口,我们新建一个SAXPraserHelper去继承DefaultHandler(DefaultHandler实现了ContentHandler接口)

下面是ContentHandler接口的常用方法

     public abstract void characters (char[] ch, int start, int length)

      这个方法来接收字符块通知,解析器通过这个方法来报告字符数据块,解析器为了提高解析效率把读到的所有字符串放到一个字符数组(ch)中,作为参数传递给character的方法中,如果想获取本次事件中读取到的字符数据,需要使用start和length属性。

    public abstract void startDocument () 接收文档开始的通知

    public abstract void endDocument () 接收文档结束的通知

    public abstract void startElement (String uri, String localName, String qName, Attributes atts) 接收文档开始的标签

    public abstract void endElement (String uri, String localName, String qName) 接收文档结束的标签


SAXPraserHelper.java文件的代码如下:

public class SAXPraserHelper extends DefaultHandler {ArrayList<WeatherInfo> list;    WeatherInfo weatherInfo;    //状态标志    final int INFOS=1;    final int CITY=2;    final int WEATHERINFO_ID = 3;    final int WEATHERINFO_TEMP = 4;    final int WEATHERINFO_WEATHER = 5;    final int WEATHERINFO_WIND = 6;    final int WEATHERINFO_NAME = 7;    final int WEATHERINFO_PM = 8;        int currentState = 0;    public ArrayList<WeatherInfo> getList() {        return list;    }    /*     * 接口字符块通知     * 处理元素的字符内容,从参数中可以获得内容     */    @Override    public void characters(char[] ch, int start, int length)            throws SAXException {    //将当前解析的字符转换成字符串        String theString = String.valueOf(ch, start, length);                switch(currentState){        case WEATHERINFO_TEMP:        weatherInfo.setTemp(theString);            currentState = 0;            break;        case WEATHERINFO_WEATHER:        weatherInfo.setWeather(theString);            currentState = 0;            break;        case WEATHERINFO_WIND:        weatherInfo.setWind(theString);            currentState = 0;            break;        case WEATHERINFO_NAME:        weatherInfo.setName(theString);            currentState = 0;            break;        case WEATHERINFO_PM:        weatherInfo.setPm(theString);            currentState = 0;            break;        default:            return;        }    }    /*     * 解析文档开始通知     */    @Override    public void startDocument() throws SAXException {        list = new ArrayList<WeatherInfo>();            }        /*     * 解析文档结束通知     */    @Override    public void endDocument() throws SAXException {        super.endDocument();    }    /*     * 解析标签结束通知     */    @Override    public void endElement(String uri, String localName, String qName)            throws SAXException {    //解析到结束的city结点时表示一个weather信息解析完毕,将其添加到list集合        if (localName.equals("city")){            list.add(weatherInfo);            return;        }    }    /*     * 解析标签开始通知     * 解析的方式是一个结点一个结点向下解析的     */    @Override    public void startElement(String uri, String localName, String qName,            Attributes attributes) throws SAXException {    /**     * 每解析到一个结点时标志一个状态     */        if(localName.equals("infos")){               currentState = INFOS;           return;           }    /**     * id是<city>结点中的属性,这里先设置     */    if(localName.equals("city")){     //开始解析一个weather信息,在这开始实例化一个对象    weatherInfo=new WeatherInfo();     for (int i = 0; i < attributes.getLength(); i++) {     if (attributes.getLocalName(i).equals("id")) {     //获得city结点里的属性,并设置                 weatherInfo.setId(Integer.parseInt(attributes.getValue(i)));                 }     }    return;          }           if(localName.equals("temp")){    currentState = WEATHERINFO_TEMP;      return;         }           if(localName.equals("weather")){    currentState = WEATHERINFO_WEATHER;      return;           }           if(localName.equals("wind")){     currentState = WEATHERINFO_WIND;    return;           }            if(localName.equals("name")){    currentState = WEATHERINFO_NAME;    return;            }            if(localName.equals("pm")){    currentState = WEATHERINFO_PM;     return;           }            currentState = 0;                return;    }}


实现一个ContentHandler一般要一下几个步骤:

1、声明一个类,继承DefaultHandler。DefaultHandler是一个基类,这个类里面简单实现了一个ContentHandler。我们只需要重写里面的方法即可。

2、重写 startDocument() 和 endDocument(),一般解析将正式解析之前的一些初始化工资放到startDocument()里面,收尾的工作放到endDocument()里面。

3、重写startElement(),XML解析器遇到XML里面的tag时就会调用这个函数。经常在这个函数内是通过localName俩进行判断而操作一些数据。

4、重写characters()方法,这是一个回调方法。解析器执行完startElement()后,解析完节点的内容后就会执行这个方法,并且参数ch[]就是节点的内容。这个例子里我们根据currentstate的不同,来判断当前那个tag的内容,并放到合适的实体类中。

5、重写endElement()方法,这个方法与startElement()相对应,解析完一个tag节点后,执行这个方法。再找个例子中,如果解析一个item结束,就将RSSIiem添加到RSSFeed中。

运行的效果如图所示:

下面总结一下SAX方式解析xml文件的主要步骤:

1、实例化一个工厂SAXParserFactory,代码如下:

SAXParserFactory factory = SAXParserFactory.newInstance();


2、实例化SAXPraser对象,创建XMLReader 解析器,代码如下:

SAXParser parser;// 实例化SAXParser对象,创建XMLReader对象,解析器parser = factory.newSAXParser();XMLReader xmlReader = parser.getXMLReader();

 

3、实例化handler处理器,代码如下:

SAXPraserHelper helperHandler = new SAXPraserHelper();

 

4、解析器注册一个事件,代码如下:

xmlReader.setContentHandler(helperHandler);

 

4、读取文件流,代码如下:

InputStream stream = is;InputSource source = new InputSource(stream);

 

5、解析文件,代码如下:

xmlReader.parse(source);


SAX解析的特点:

优点:不用实现调入整个文档,占用资源少。尤其在嵌入式环境中,如android,极力推荐使用SAX解析。

缺点:不像DOM解析一样将文档长期驻留在内存中,数据不是持久的。如果事件过后没有保存数据,数据就会丢失。

使用场合:机器有性能限制。

 

 

第二种方式:pull方式解析xml文件

它和sax解析一样,也是采用事件驱动进行解析的,不同的是,在PULL解析过程中返回的是数字,且我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法当pull解析器,开始解析之后,我们可以调用它的next()方法,来获取下一个解析事件(就是开始文档,结束文档,开始标签,结束标签),当处于某个元素时可以调用XmlPullParser的getAttributte()方法来获取属性的值,也可调用它的nextText()获取本节点的值。


 WeatherService.java文件中的代码:

public static ArrayList<WeatherInfo> getWeatherInfoByPull(InputStream is)throws Exception {ArrayList<WeatherInfo> infos = null;WeatherInfo info = null;        //获得一个XmlPullParserFactory工厂XmlPullParserFactory fac = XmlPullParserFactory.newInstance();XmlPullParser parser = fac.newPullParser();        //设置XmlPullParser解析字符方式parser.setInput(is, "utf-8");//Returns the type of the current event (START_TAG, END_TAG, TEXT, etc.)int type = parser.getEventType();while (type != XmlPullParser.END_DOCUMENT) {switch (type) {case XmlPullParser.START_TAG:if ("infos".equals(parser.getName())) {infos = new ArrayList<WeatherInfo>();} else if ("city".equals(parser.getName())) {info = new WeatherInfo();info.setId(Integer.parseInt(parser.getAttributeValue(0)));} else if ("temp".equals(parser.getName())) {info.setTemp(parser.nextText());} else if ("weather".equals(parser.getName())) {info.setWeather(parser.nextText());} else if ("wind".equals(parser.getName())) {info.setWind(parser.nextText());} else if ("name".equals(parser.getName())) {info.setName(parser.nextText());} else if ("pm".equals(parser.getName())) {info.setPm(parser.nextText());}break;case XmlPullParser.END_TAG:if ("city".equals(parser.getName())) {infos.add(info);info = null;}break;}type = parser.next();}return infos;}

运行效果:


pull解析的主要步骤:

1、创建一个XmlPullParserFactory,代码如下:

XmlPullParserFactory fac = XmlPullParserFactory.newInstance();


2、通过XmlPullParserFactory工厂获得一个XmlPullParser实例,代码如下:

XmlPullParser parser = fac.newPullParser();


3、设置格式,代码如下:

parser.setInput(is, "utf-8");


4、通过parser获取解析事件返回的类型,并根据返回的类型作出相应的处理,代码如下:

int type = parser.getEventType();while (type != XmlPullParser.END_DOCUMENT) {    switch (type) { case XmlPullParser.START_TAG: if ("infos".equals(parser.getName())) { infos = new ArrayList<WeatherInfo>(); } else if ("city".equals(parser.getName())) { info = new WeatherInfo(); info.setId(Integer.parseInt(parser.getAttributeValue(0))); } else if ("temp".equals(parser.getName())) { info.setTemp(parser.nextText()); } else if ("weather".equals(parser.getName())) { info.setWeather(parser.nextText()); } else if ("wind".equals(parser.getName())) { info.setWind(parser.nextText()); } else if ("name".equals(parser.getName())) { info.setName(parser.nextText()); } else if ("pm".equals(parser.getName())) { info.setPm(parser.nextText()); } break; case XmlPullParser.END_TAG: if ("city".equals(parser.getName())) { infos.add(info); info = null; } break;}

5、解析下一个结点,代码如下:

type = parser.next();


 

第三种方式:dom方式解析xml文件

dom解析是先把dom全部文件读入到内存中,然后使用dom的api遍历所有数据,检索想要的数据,这种方式显然是一种比较消耗内存的方式,对于像手机这样的移动设备来讲,内存是非常有限的,所以对于比较大的XML文件,不推荐使用这种方式,但是Dom也有它的优点,它比较直观,在一些方面比SAX方式比较简单。在xml文档比较小的情况下也可以考虑使用dom方式。

WeatherService.java文件中的代码:

public static ArrayList<WeatherInfo> getWeatherInfoByDom(InputStream is)throws Exception {ArrayList<WeatherInfo> list = new ArrayList<WeatherInfo>();// 得到 DocumentBuilderFactory 对象, 由该对象可以得到 DocumentBuilder 对象DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();try {// 得到DocumentBuilder对象DocumentBuilder builder = factory.newDocumentBuilder();// 得到代表整个xml的Document对象Document document = builder.parse(is);// 得到 "根节点"--infosElement root = document.getDocumentElement();// 获取根节点的所有city的节点NodeList city = root.getElementsByTagName("city");// 遍历所有节点System.out.println("city.getLength()    " + city.getLength());for (int i = 0; i < city.getLength(); i++) {WeatherInfo weather = new WeatherInfo();Element item = (Element) city.item(i);weather.setId(Integer.parseInt(item.getAttribute("id")));// 获取特定位置的node 这里获得的还是city结点Node element2 = (Node) city.item(i);//获得city结点的所有子节点NodeList childList = element2.getChildNodes();System.out.println("childList.getLength()    " + childList.getLength());for (int j = 0; j < childList.getLength(); j++) {Node node =childList.item(j);String tagName = node.getNodeName();System.out.println("tagName    " + tagName);if (tagName.equals("temp")) {String temp = node.getTextContent();weather.setTemp(temp);}if (tagName.equals("weather")) {String wea = node.getTextContent();weather.setWeather(wea);}if (tagName.equals("wind")) {String wind = node.getTextContent();weather.setWind(wind);}if (tagName.equals("name")) {String name = node.getTextContent();weather.setName(name);}if (tagName.equals("pm")) {String pm = node.getTextContent();weather.setPm(pm);}}list.add(weather);}} catch (Exception e) {e.printStackTrace();}return list;}

运行效果:


dom解析的特点:

dom(文件对象模型)解析:解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以根据DOM接口来操作这个树结构了。

优点:整个文档读入内存,方便操作:支持修改、删除和重现排列等多种功能。

缺点:将整个文档读入内存中,保留了过多的不需要的节点,浪费内存和空间。

使用场合:一旦读入文档,还需要多次对文档进行操作,并且在硬件资源充足的情况下(内存,CPU)。

 

dom解析的步骤:

1、调用 DocumentBuilderFactory.newInstance() 方法得到 DOM 解析器工厂类实例,代码如下:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

 

2、调用解析器工厂实例类的 newDocumentBuilder() 方法得到 DOM 解析器对象,代码如下:

DocumentBuilder builder = factory.newDocumentBuilder();

 

3、调用 DOM 解析器对象的 parse() 方法解析 XML 文档得到代表整个文档的 Document 对象,代码如下:

Document document = builder.parse(is);


4、根据解析到的结点作出相应的处理

 

几种解析技术的比较与总结: 
对于Android的移动设备而言,因为设备的资源比较宝贵,内存是有限的,所以我们需要选择适合的技术来解析XML,这样有利于提高访问的速度。

dom在处理XML文件时,将XML文件解析成树状结构并放入内存中进行处理。当XML文件较小时,我们可以选DOM,因为它简单、直观。

sax则是以事件作为解析XML文件的模式,它将XML文件转化成一系列的事件,由不同的事件处理器来决定如何处理。XML文件较大时,选择SAX技术是比较合理的。虽然代码量有些大,但是它不需要将所有的XML文件加载到内存中。这样对于有限的Android内存更有效,而且Android提供了一种传统的SAX使用方法以及一个便捷的SAX包装器。 使用AndroidutilXml类,从示例中可以看出,会比使用 SAX来得简单。

pull解析并未像SAX解析那样监听元素的结束,而是在开始处完成了大部分处理。这有利于提早读取XML文件,可以极大的减少解析时间,这种优化对于连接速度较漫的移动设备而言尤为重要。对于XML文档较大但只需要文档的一部分时,XML Pull解析器则是更为有效的方法。

 

 

 源码下载地址:http://download.csdn.net/detail/dangnianmingyue_gg/9081495


 

 

 

0 0
原创粉丝点击