Android中解析XML

来源:互联网 发布:淘宝直播要钱吗 编辑:程序博客网 时间:2024/06/05 23:04

原文地址:http://www.cnblogs.com/hanyonglu/archive/2012/02/28/2370675.html

本文介绍在Android平台中实现对XML的三种解析方式。

  XML在各种开发中都广泛应用,Android也不例外。作为承载数据的一个重要角色,如何读写XML成为Android开发中一项重要的技能。

  在Android中,常见的XML解析器分别为DOM解析器、SAX解析器和PULL解析器,下面,我将一一向大家详细介绍。

 

第一种方式:DOM解析器:

DOM是基于树形结构的的节点或信息片段的集合,允许开发人员使用DOM API遍历XML树、检索所需数据。分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。Android完全支持DOM 解析。利用DOM中的对象,可以对XML文档进行读取、搜索、修改、添加和删除等操作。

 

DOM的工作原理:使用DOM对XML文件进行操作时,首先要解析文件,将文件分为独立的元素、属性和注释等,然后以节点树的形式在内存中对XML文件进行表示,就可以通过节点树访问文档的内容,并根据需要修改文档——这就是DOM的工作原理。

 

DOM实现时首先为XML文档的解析定义一组接口,解析器读入整个文档,然后构造一个驻留内存的树结构,这样代码就可以使用DOM接口来操作整个树结构。 

 

由于DOM在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会很耗资源。 当然,如果XML文件的内容比较小,采用DOM是可行的。

 

常用的DoM接口和类:

  Document:该接口定义分析并创建DOM文档的一系列方法,它是文档树的根,是操作DOM的基础。  

Element:该接口继承Node接口,提供了获取、修改XML元素名字和属性的方法。

Node:该接口提供处理并获取节点和子节点值的方法。

NodeList:提供获得节点个数和当前节点的方法。这样就可以迭代地访问各个节点。

DOMParser:该类是Apache的Xerces中的DOM解析器类,可直接解析XML文件。

 

下面是DOM的解析流程:

 

 

第二种方式:SAX解析器:

  SAX(Simple API for XML)解析器是一种基于事件的解析器,事件驱动的流式解析方式是,从文件的开始顺序解析到文档的结束,不可暂停或倒退。它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。  

 

  SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用。

 

SAX的工作原理:SAX的工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。

 

  在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通过parser()方法来解析XML文档,并产生事件。事件处理器是org.xml.sax包中ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口。XMLReader通过相应事件处理器注册方法setXXXX()来完成的与ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口的连接。

 

常用的SAX接口和类:

 Attrbutes:用于得到属性的个数、名字和值。  

 ContentHandler:定义与文档本身关联的事件(例如,开始和结束标记)。大多数应用程序都注册这些事件。

 DTDHandler:定义与DTD关联的事件。它没有定义足够的事件来完整地报告DTD。如果需要对DTD进行语法分析,请使用可选的DeclHandler。

 DeclHandler是SAX的扩展。不是所有的语法分析器都支持它。

 EntityResolver:定义与装入实体关联的事件。只有少数几个应用程序注册这些事件。

 ErrorHandler:定义错误事件。许多应用程序注册这些事件以便用它们自己的方式报错。

 DefaultHandler:它提供了这些接LI的缺省实现。在大多数情况下,为应用程序扩展DefaultHandler并覆盖相关的方法要比直接实现一个接口更容易。

 

 详见下表:

 

  

 可知,我们需要XmlReader 以及DefaultHandler来配合解析xml。

 下面是SAX的解析流程:

  

 

 

第三种方式:PULL解析器: 

      Android并未提供对Java StAX API的支持。但是,Android附带了一个pull解析器,其工作方式类似于StAX。它允许用户的应用程序代码从解析器中获取事件,这与SAX解析器自动将事件推入处理程序相反。 

 

PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中返回的是数字,且我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。

下面是PULL解析XML的过程: 

        读取到xml的声明返回 START_DOCUMENT;

读取到xml的结束返回 END_DOCUMENT ;

读取到xml的开始标签返回 START_TAG

读取到xml的结束标签返回 END_TAG

读取到xml的文本返回 TEXT

  

PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器,Android官方推荐开发者们使用Pull解析技术。Pull解析技术是第三方开发的开源技术,它同样可以应用于JavaSE开发。

 

      PULL 的工作原理:XML pull提供了开始元素和结束元素。当某个元素开始时,我们可以调用parser.nextText从XML文档中提取所有字符数据。当解释到一个文档结束时,自动生成EndDocument事件。

 

      常用的XML pull的接口和类: 

XmlPullParser:XML pull解析器是一个在XMLPULL VlAP1中提供了定义解析功能的接口。

XmlSerializer:它是一个接口,定义了XML信息集的序列。

XmlPullParserFactory:这个类用于在XMPULL V1 API中创建XML Pull解析器。

XmlPullParserException:抛出单一的XML pull解析器相关的错误。

 

PULL的解析流程如下:

     

      [附加]第四种方式: Android.util.Xml类

在Android API中,另外提供了Android.util.Xml类,同样可以解析XML文件,使用方法类似SAX,也都需编写Handler来处理XML的解析,但是在使用上却比SAX来得简单 ,如下所示:
以android.util.XML实现XML解析 ,
MyHandler myHandler=new MyHandler0;
android.util.Xm1.parse(ur1.openC0nnection().getlnputStream0,Xm1.Encoding.UTF-8,myHandler);

 

      下面是一个参考文档river.xml,放在assets目录.如下: 

复制代码
<?xml version="1.0" encoding="utf-8"?> 

<rivers>
 <river name="灵渠" length="605">
     <introduction>
      灵渠在广西壮族自治区兴安县境内,是世界上最古老的运河之一,有着“世界古代水利建筑明珠”的美誉。灵渠古称秦凿渠、零渠、陡河、兴安运河,于公元前214年凿成通航,距今已2217年,仍然发挥着功用。
     </introduction>
      <imageurl>
      http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg
     </imageurl>
   </river> 
   
   <river name="胶莱运河" length="200">
     <introduction>
      胶莱运河南起黄海灵山海口,北抵渤海三山岛,流经现胶南、胶州、平度、高密、昌邑和莱州等,全长200公里,流域面积达5400平方公里,南北贯穿山东半岛,沟通黄渤两海。胶莱运河自平度姚家村东的分水岭南北分流。南流由麻湾口入胶州湾,为南胶莱河,长30公里。北流由海仓口入莱州湾,为北胶莱河,长100余公里。
     </introduction>
      <imageurl>
      http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg
     </imageurl>
   </river>
   
   <river name="苏北灌溉总渠" length="168"> 
     <introduction>
      位于淮河下游江苏省北部,西起洪泽湖边的高良涧,流经洪泽,青浦、淮安,阜宁、射阳,滨海等六县(区),东至扁担港口入海的大型人工河道。全长168km。
     </introduction>
      <imageurl>
      http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg
     </imageurl>
   </river>
 </rivers>

复制代码
  

      采用DOM解析时具体处理步骤是:

首先利用DocumentBuilderFactory创建一个DocumentBuilderFactory实例
然后利用DocumentBuilderFactory创建DocumentBuilder

然后加载XML文档(Document,
然后获取文档的根结点(Element)
然后获取根结点中所有子节点的列表(NodeList),
然后使用再获取子节点列表中的需要读取的结点。

 

  当然我们观察节点,我需要用一个River对象来保存数据,抽象出River 
复制代码
public class River implements Serializable {     

   privatestaticfinallong serialVersionUID = 1L; 
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getLength() {
        return length;
    }
    public void setLength(int length) {
        this.length = length;
    }
    public String getIntroduction() {
        return introduction;
    }
    public void setIntroduction(String introduction) {
        this.introduction = introduction;
    }
    public String getImageurl() {
        return imageurl;
    }
    public void setImageurl(String imageurl) {
        this.imageurl = imageurl;
    }
    private int length;
    private String introduction;
    private String imageurl; 
}

复制代码

 

下面我们就开始读取xml文档对象,并添加进List中:

 

代码如下: 我们这里是使用assets中的river.xml文件,那么就需要读取这个xml文件,返回输入流。 读取方法为:inputStream=this.context.getResources().getAssets().open(fileName); 参数是xml文件路径,当然默认的是assets目录为根目录。

 

然后可以用DocumentBuilder对象的parse方法解析输入流,并返回document对象,然后再遍历doument对象的节点属性。
  

复制代码
   //获取全部河流数据 

   /**   

     * 参数fileName:为xml文档路径
     
*/
    public List<River> getRiversFromXml(String fileName){
        List<River> rivers=new ArrayList<River>();
        DocumentBuilderFactory factory=null;
        DocumentBuilder builder=null;
        Document document=null;
        InputStream inputStream=null;
        //首先找到xml文件
        factory=DocumentBuilderFactory.newInstance();
        try {
            //找到xml,并加载文档
            builder=factory.newDocumentBuilder();
            inputStream=this.context.getResources().getAssets().open(fileName);
            document=builder.parse(inputStream);
            //找到根Element
             Element root=document.getDocumentElement();
             NodeList nodes=root.getElementsByTagName(RIVER);
            //遍历根节点所有子节点,rivers 下所有river
             River river=null;
             for(int i=0;i<nodes.getLength();i++){
                     river=new River(); 
                     //获取river元素节点
                     Element riverElement=(Element)(nodes.item(i));
                     //获取river中name属性值
                     river.setName(riverElement.getAttribute(NAME));
                     river.setLength(Integer.parseInt(riverElement.getAttribute(LENGTH)));
                     //获取river下introduction标签
                     Element introduction=(Element)riverElement.getElementsByTagName(INTRODUCTION).item(0);
                     river.setIntroduction(introduction.getFirstChild().getNodeValue());
                     Element imageUrl=(Element)riverElement.getElementsByTagName(IMAGEURL).item(0);
                     river.setImageurl(imageUrl.getFirstChild().getNodeValue()); 
                 rivers.add(river);
             }
        }catch (IOException e){
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        }
         catch (ParserConfigurationException e) {
            e.printStackTrace();
        }finally{
            try {
                inputStream.close();
            } catch (IOException e) {    
                e.printStackTrace();
            }
        }
        return rivers;
    }

复制代码

在这里添加到List中, 然后我们使用ListView将他们显示出来。如图所示: 

 


  采用SAX解析时具体处理步骤是:

1 创建SAXParserFactory对象

2 根据SAXParserFactory.newSAXParser()方法返回一个SAXParser解析器

3 根据SAXParser解析器获取事件源对象XMLReader

4 实例化一个DefaultHandler对象

5 连接事件源对象XMLReader到事件处理类DefaultHandler中

6 调用XMLReader的parse方法从输入源中获取到的xml数据

7 通过DefaultHandler返回我们需要的数据集合。

 

代码如下:

复制代码
  public List<River> parse(String xmlPath){

List<River> rivers=null;
        SAXParserFactory factory=SAXParserFactory.newInstance();
        try {
            SAXParser parser=factory.newSAXParser();
            //获取事件源
            XMLReader xmlReader=parser.getXMLReader();
            //设置处理器
            RiverHandler handler=new RiverHandler();
            xmlReader.setContentHandler(handler);
            //解析xml文档
            
//xmlReader.parse(new InputSource(new URL(xmlPath).openStream()));
            xmlReader.parse(new InputSource(this.context.getAssets().open(xmlPath)));
            rivers=handler.getRivers();    
        } catch (ParserConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        return rivers;
    }

复制代码

 

重点在于DefaultHandler对象中对每一个元素节点,属性,文本内容,文档内容进行处理。

 

前面说过DefaultHandler是基于事件处理模型的,基本处理方式是:当SAX解析器导航到文档开始标签时回调startDocument方法,导航到文档结束标签时回调endDocument方法。当SAX解析器导航到元素开始标签时回调startElement方法,导航到其文本内容时回调characters方法,导航到标签结束时回调endElement方法。

 

根据以上的解释,我们可以得出以下处理xml文档逻辑:

1:当导航到文档开始标签时,在回调函数startDocument中,可以不做处理,当然你可以验证下UTF-8等等。

2:当导航到rivers开始标签时,在回调方法startElement中可以实例化一个集合用来存贮list,不过我们这里不用,因为在构造函数中已经实例化了。

3:导航到river开始标签时,就说明需要实例化River对象了,当然river标签中还有name ,length属性,因此实例化River后还必须取出属性值,attributes.getValue(NAME),同时赋予river对象中,同时添加为导航到的river标签添加一个boolean为真的标识,用来说明导航到了river元素。

4:当然有river标签内还有子标签(节点),但是SAX解析器是不知道导航到什么标签的,它只懂得开始,结束而已。那么如何让它认得我们的各个标签呢?当然需要判断了,于是可以使用回调方法startElement中的参数String localName,把我们的标签字符串与这个参数比较下,就可以了。我们还必须让SAX知道,现在导航到的是某个标签,因此添加一个true属性让SAX解析器知道。

5:它还会导航到文本内标签,(就是<img></img>里面的内容),回调方法characters,我们一般在这个方法中取出就是<img></img>里面的内容,并保存。 6:当然它是一定会导航到结束标签</river> 或者</rivers>的,如果是</river>标签,记得把river对象添加进list中。如果是river中的子标签</introduction>,就把前面设置标记导航到这个标签的boolean标记设置为false. 按照以上实现思路,可以实现如下代码:

复制代码
  /**导航到开始标签触发**/

        publicvoid startElement (String uri, String localName, String qName, Attributes attributes){ 
         String tagName=localName.length()!=0?localName:qName;
         tagName=tagName.toLowerCase().trim();
         //如果读取的是river标签开始,则实例化River
         if(tagName.equals(RIVER)){
             isRiver=true;
             river=new River();
                /**导航到river开始节点后**/
                river.setName(attributes.getValue(NAME));
                river.setLength(Integer.parseInt(attributes.getValue(LENGTH)));
         }
         //然后读取其他节点
          if(isRiver){ 
              if(tagName.equals(INTRODUCTION)){
                 xintroduction=true;
             }else if(tagName.equals(IMAGEURL)){
                 ximageurl=true;
             }  
         }  
        }
        
        /**导航到结束标签触发**/
        public void endElement (String uri, String localName, String qName){
         String tagName=localName.length()!=0?localName:qName;
         tagName=tagName.toLowerCase().trim();
         
        //如果读取的是river标签结束,则把River添加进集合中
         if(tagName.equals(RIVER)){
             isRiver=true;
             rivers.add(river);
         }
         //然后读取其他节点
          if(isRiver){ 
              if(tagName.equals(INTRODUCTION)){
                 xintroduction=false;
             }else if(tagName.equals(IMAGEURL)){
                 ximageurl=false;
             } 
          }   
        } 
        
        //这里是读取到节点内容时候回调
        public void characters (char[] ch, int start, int length){
            //设置属性值
                if(xintroduction){
                     //解决null问题
                     river.setIntroduction(river.getIntroduction()==null?"":river.getIntroduction()+new String(ch,start,length));
                 }else if(ximageurl){
                     //解决null问题
                     river.setImageurl(river.getImageurl()==null?"":river.getImageurl()+new String(ch,start,length));
                 }    
        } 

复制代码

 

运行效果跟上例DOM 运行效果相同。

 

采用PULL解析基本处理方式:

当PULL解析器导航到文档开始标签时就开始实例化list集合用来存贮数据对象。导航到元素开始标签时回判断元素标签类型,如果是river标签,则需要实例化River对象了,如果是其他类型,则取得该标签内容并赋予River对象。当然它也会导航到文本标签,不过在这里,我们可以不用。

 

根据以上的解释,我们可以得出以下处理xml文档逻辑:

1:当导航到XmlPullParser.START_DOCUMENT,可以不做处理,当然你可以实例化集合对象等等。

2:当导航到XmlPullParser.START_TAG,则判断是否是river标签,如果是,则实例化river对象,并调用getAttributeValue方法获取标签中属性值。

3:当导航到其他标签,比如Introduction时候,则判断river对象是否为空,如不为空,则取出Introduction中的内容,nextText方法来获取文本节点内容

4:当然啦,它一定会导航到XmlPullParser.END_TAG的,有开始就要有结束嘛。在这里我们就需要判读是否是river结束标签,如果是,则把river对象存进list集合中了,并设置river对象为null.

由以上的处理逻辑,我们可以得出以下代码:

复制代码
    public List<River> parse(String xmlPath){

List<River> rivers=new ArrayList<River>();
        River river=null;
        InputStream inputStream=null;    
        //获得XmlPullParser解析器
        XmlPullParser xmlParser = Xml.newPullParser();   
        try {
            //得到文件流,并设置编码方式
            inputStream=this.context.getResources().getAssets().open(xmlPath);
            xmlParser.setInput(inputStream, "utf-8");
            //获得解析到的事件类别,这里有开始文档,结束文档,开始标签,结束标签,文本等等事件。
            int evtType=xmlParser.getEventType();
         //一直循环,直到文档结束    
         while(evtType!=XmlPullParser.END_DOCUMENT){ 
            switch(evtType){ 
            case XmlPullParser.START_TAG:
                String tag = xmlParser.getName(); 
                //如果是river标签开始,则说明需要实例化对象了
                if (tag.equalsIgnoreCase(RIVER)) { 
                   river = new River(); 
                  //取出river标签中的一些属性值
                  river.setName(xmlParser.getAttributeValue(null, NAME));
                  river.setLength(Integer.parseInt(xmlParser.getAttributeValue(null, LENGTH)));
                }else if(river!=null){
                    //如果遇到introduction标签,则读取它内容
                    if(tag.equalsIgnoreCase(INTRODUCTION)){
                    river.setIntroduction(xmlParser.nextText());
                    }else if(tag.equalsIgnoreCase(IMAGEURL)){
                        river.setImageurl(xmlParser.nextText());
                    }
                }
                break;
                
           case XmlPullParser.END_TAG:
             //如果遇到river标签结束,则把river对象添加进集合中
               if (xmlParser.getName().equalsIgnoreCase(RIVER) && river != null) { 
                   rivers.add(river); 
                   river = null
               }
                break
                default:break;
            }
            //如果xml没有结束,则导航到下一个river节点
            evtType=xmlParser.next();
         }
        } catch (XmlPullParserException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } 
        return rivers; 

 } 

复制代码


运行效果和上面的一样。

 

几种解析技术的比较与总结:  

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

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

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

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

 

以上便是在Android平台中对XML进行解析的方法和使用过程。 

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

原文地址:http://blog.csdn.net/liuhe688/article/details/6415593/

XML在各种开发中都广泛应用,Android也不例外。作为承载数据的一个重要角色,如何读写XML成为Android开发中一项重要的技能。今天就由我向大家介绍一下在Android平台下几种常见的XML解析和创建的方法。

在Android中,常见的XML解析器分别为SAX解析器、DOM解析器和PULL解析器,下面,我将一一向大家详细介绍。

SAX解析器:

SAX(Simple API for XML)解析器是一种基于事件的解析器,它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。

SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用。

DOM解析器:

DOM是基于树形结构的的节点或信息片段的集合,允许开发人员使用DOM API遍历XML树、检索所需数据。分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。

由于DOM在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会很耗资源。

PULL解析器:

PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中,我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器。

以上三种解析器,都是非常实用的解析器,我将会一一介绍。我们将会使用这三种解析技术完成一项共同的任务。

我们新建一个项目,项目结构如下:

我会在项目的assets目录中放置一个XML文档books.xml,内容如下:

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <books>  
  3.     <book>  
  4.         <id>1001</id>  
  5.         <name>Thinking In Java</name>  
  6.         <price>80.00</price>  
  7.     </book>  
  8.     <book>  
  9.         <id>1002</id>  
  10.         <name>Core Java</name>  
  11.         <price>90.00</price>  
  12.     </book>  
  13.     <book>  
  14.         <id>1003</id>  
  15.         <name>Hello, Andriod</name>  
  16.         <price>100.00</price>  
  17.     </book>  
  18. </books>  
然后我们分别使用以上三种解析技术解析文档,得到一个List<Book>的对象,先来看一下Book.java的代码:

[java] view plain copy
  1. package com.scott.xml.model;  
  2.   
  3. public class Book {  
  4.     private int id;  
  5.     private String name;  
  6.     private float price;  
  7.   
  8.     public int getId() {  
  9.         return id;  
  10.     }  
  11.   
  12.     public void setId(int id) {  
  13.         this.id = id;  
  14.     }  
  15.   
  16.     public String getName() {  
  17.         return name;  
  18.     }  
  19.   
  20.     public void setName(String name) {  
  21.         this.name = name;  
  22.     }  
  23.   
  24.     public float getPrice() {  
  25.         return price;  
  26.     }  
  27.   
  28.     public void setPrice(float price) {  
  29.         this.price = price;  
  30.     }  
  31.   
  32.     @Override  
  33.     public String toString() {  
  34.         return "id:" + id + ", name:" + name + ", price:" + price;  
  35.     }  
  36. }  
最后,我们还要把这个集合对象中的数据生成一个新的XML文档,如图:

生成的XML结构跟原始文档略有不同,是下面这种格式:

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <books>  
  3.     <book id="1001">  
  4.         <name>Thinking In Java</name>  
  5.         <price>80.0</price>  
  6.     </book>  
  7.     <book id="1002">  
  8.         <name>Core Java</name>  
  9.         <price>90.0</price>  
  10.     </book>  
  11.     <book id="1003">  
  12.         <name>Hello, Andriod</name>  
  13.         <price>100.0</price>  
  14.     </book>  
  15. </books>  
接下来,就该介绍操作过程了,我们先为解析器定义一个BookParser接口,每种类型的解析器需要实现此接口。BookParser.java代码如下:

[java] view plain copy
  1. package com.scott.xml.parser;  
  2.   
  3. import java.io.InputStream;  
  4. import java.util.List;  
  5.   
  6. import com.scott.xml.model.Book;  
  7.   
  8. public interface BookParser {  
  9.     /** 
  10.      * 解析输入流 得到Book对象集合 
  11.      * @param is 
  12.      * @return 
  13.      * @throws Exception 
  14.      */  
  15.     public List<Book> parse(InputStream is) throws Exception;  
  16.   
  17.     /** 
  18.      * 序列化Book对象集合 得到XML形式的字符串 
  19.      * @param books 
  20.      * @return 
  21.      * @throws Exception 
  22.      */  
  23.     public String serialize(List<Book> books) throws Exception;  
  24. }  
好了,我们就该一个一个的实现该接口,完成我们的解析过程。

使用SAX解析器:

SaxBookParser.Java代码如下:

[java] view plain copy
  1. package com.scott.xml.parser;  
  2.   
  3. import java.io.InputStream;  
  4. import java.io.StringWriter;  
  5. import java.util.ArrayList;  
  6. import java.util.List;  
  7.   
  8. import javax.xml.parsers.SAXParser;  
  9. import javax.xml.parsers.SAXParserFactory;  
  10. import javax.xml.transform.OutputKeys;  
  11. import javax.xml.transform.Result;  
  12. import javax.xml.transform.Transformer;  
  13. import javax.xml.transform.TransformerFactory;  
  14. import javax.xml.transform.sax.SAXTransformerFactory;  
  15. import javax.xml.transform.sax.TransformerHandler;  
  16. import javax.xml.transform.stream.StreamResult;  
  17.   
  18. import org.xml.sax.Attributes;  
  19. import org.xml.sax.SAXException;  
  20. import org.xml.sax.helpers.AttributesImpl;  
  21. import org.xml.sax.helpers.DefaultHandler;  
  22.   
  23. import com.scott.xml.model.Book;  
  24.   
  25. public class SaxBookParser implements BookParser {  
  26.   
  27.     @Override  
  28.     public List<Book> parse(InputStream is) throws Exception {  
  29.         SAXParserFactory factory = SAXParserFactory.newInstance();  //取得SAXParserFactory实例  
  30.         SAXParser parser = factory.newSAXParser();                  //从factory获取SAXParser实例  
  31.         MyHandler handler = new MyHandler();                        //实例化自定义Handler  
  32.         parser.parse(is, handler);                                  //根据自定义Handler规则解析输入流  
  33.         return handler.getBooks();  
  34.     }  
  35.   
  36.     @Override  
  37.     public String serialize(List<Book> books) throws Exception {  
  38.         SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();//取得SAXTransformerFactory实例  
  39.         TransformerHandler handler = factory.newTransformerHandler();           //从factory获取TransformerHandler实例  
  40.         Transformer transformer = handler.getTransformer();                     //从handler获取Transformer实例  
  41.         transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");            // 设置输出采用的编码方式  
  42.         transformer.setOutputProperty(OutputKeys.INDENT, "yes");                // 是否自动添加额外的空白  
  43.         transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");   // 是否忽略XML声明  
  44.   
  45.         StringWriter writer = new StringWriter();  
  46.         Result result = new StreamResult(writer);  
  47.         handler.setResult(result);  
  48.   
  49.         String uri = "";    //代表命名空间的URI 当URI无值时 须置为空字符串  
  50.         String localName = "";  //命名空间的本地名称(不包含前缀) 当没有进行命名空间处理时 须置为空字符串  
  51.   
  52.         handler.startDocument();  
  53.         handler.startElement(uri, localName, "books"null);  
  54.   
  55.         AttributesImpl attrs = new AttributesImpl();    //负责存放元素的属性信息  
  56.         char[] ch = null;  
  57.         for (Book book : books) {  
  58.             attrs.clear();  //清空属性列表  
  59.             attrs.addAttribute(uri, localName, "id""string", String.valueOf(book.getId()));//添加一个名为id的属性(type影响不大,这里设为string)  
  60.             handler.startElement(uri, localName, "book", attrs);    //开始一个book元素 关联上面设定的id属性  
  61.   
  62.             handler.startElement(uri, localName, "name"null); //开始一个name元素 没有属性  
  63.             ch = String.valueOf(book.getName()).toCharArray();  
  64.             handler.characters(ch, 0, ch.length);   //设置name元素的文本节点  
  65.             handler.endElement(uri, localName, "name");  
  66.   
  67.             handler.startElement(uri, localName, "price"null);//开始一个price元素 没有属性  
  68.             ch = String.valueOf(book.getPrice()).toCharArray();  
  69.             handler.characters(ch, 0, ch.length);   //设置price元素的文本节点  
  70.             handler.endElement(uri, localName, "price");  
  71.   
  72.             handler.endElement(uri, localName, "book");  
  73.         }  
  74.         handler.endElement(uri, localName, "books");  
  75.         handler.endDocument();  
  76.   
  77.         return writer.toString();  
  78.     }  
  79.   
  80.     //需要重写DefaultHandler的方法  
  81.     private class MyHandler extends DefaultHandler {  
  82.   
  83.         private List<Book> books;  
  84.         private Book book;  
  85.         private StringBuilder builder;  
  86.   
  87.         //返回解析后得到的Book对象集合  
  88.         public List<Book> getBooks() {  
  89.             return books;  
  90.         }  
  91.   
  92.         @Override  
  93.         public void startDocument() throws SAXException {  
  94.             super.startDocument();  
  95.             books = new ArrayList<Book>();  
  96.             builder = new StringBuilder();  
  97.         }  
  98.   
  99.         @Override  
  100.         public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {  
  101.             super.startElement(uri, localName, qName, attributes);  
  102.             if (localName.equals("book")) {  
  103.                 book = new Book();  
  104.             }  
  105.             builder.setLength(0);   //将字符长度设置为0 以便重新开始读取元素内的字符节点  
  106.         }  
  107.   
  108.         @Override  
  109.         public void characters(char[] ch, int start, int length) throws SAXException {  
  110.             super.characters(ch, start, length);  
  111.             builder.append(ch, start, length);  //将读取的字符数组追加到builder中  
  112.         }  
  113.   
  114.         @Override  
  115.         public void endElement(String uri, String localName, String qName) throws SAXException {  
  116.             super.endElement(uri, localName, qName);  
  117.             if (localName.equals("id")) {  
  118.                 book.setId(Integer.parseInt(builder.toString()));  
  119.             } else if (localName.equals("name")) {  
  120.                 book.setName(builder.toString());  
  121.             } else if (localName.equals("price")) {  
  122.                 book.setPrice(Float.parseFloat(builder.toString()));  
  123.             } else if (localName.equals("book")) {  
  124.                 books.add(book);  
  125.             }  
  126.         }  
  127.     }  
  128. }  
代码中,我们定义了自己的事件处理逻辑,重写了DefaultHandler的几个重要的事件方法。下面我为大家着重介绍一下DefaultHandler的相关知识。DefaultHandler是一个事件处理器,可以接收解析器报告的所有事件,处理所发现的数据。它实现了EntityResolver接口、DTDHandler接口、ErrorHandler接口和ContentHandler接口。这几个接口代表不同类型的事件处理器。我们着重介绍一下ContentHandler接口。结构如图:

这几个比较重要的方法已被我用红线标注,DefaultHandler实现了这些方法,但在方法体内没有做任何事情,因此我们在使用时必须覆写相关的方法。最重要的是startElement方法、characters方法和endElement方法。当执行文档时遇到起始节点,startElement方法将会被调用,我们可以获取起始节点相关信息;然后characters方法被调用,我们可以获取节点内的文本信息;最后endElement方法被调用,我们可以做收尾的相关操作。

最后,我们需要调用SAX解析程序,这个步骤在MainActivity中完成:

[java] view plain copy
  1. package com.scott.xml;  
  2.   
  3. import java.io.FileOutputStream;  
  4. import java.io.InputStream;  
  5. import java.util.List;  
  6.   
  7. import android.app.Activity;  
  8. import android.content.Context;  
  9. import android.os.Bundle;  
  10. import android.util.Log;  
  11. import android.view.View;  
  12. import android.widget.Button;  
  13.   
  14. import com.scott.xml.model.Book;  
  15. import com.scott.xml.parser.BookParser;  
  16. import com.scott.xml.parser.SaxBookParser;  
  17.   
  18. public class MainActivity extends Activity {  
  19.   
  20.     private static final String TAG = "XML";  
  21.   
  22.     private BookParser parser;  
  23.     private List<Book> books;  
  24.   
  25.     @Override  
  26.     public void onCreate(Bundle savedInstanceState) {  
  27.         super.onCreate(savedInstanceState);  
  28.         setContentView(R.layout.main);  
  29.   
  30.         Button readBtn = (Button) findViewById(R.id.readBtn);  
  31.         Button writeBtn = (Button) findViewById(R.id.writeBtn);  
  32.   
  33.         readBtn.setOnClickListener(new View.OnClickListener() {  
  34.             @Override  
  35.             public void onClick(View v) {  
  36.                 try {  
  37.                     InputStream is = getAssets().open("books.xml");  
  38.                     parser = new SaxBookParser();  //创建SaxBookParser实例  
  39.                     books = parser.parse(is);  //解析输入流  
  40.                     for (Book book : books) {  
  41.                         Log.i(TAG, book.toString());  
  42.                     }  
  43.                 } catch (Exception e) {  
  44.                     Log.e(TAG, e.getMessage());  
  45.                 }  
  46.             }  
  47.         });  
  48.         writeBtn.setOnClickListener(new View.OnClickListener() {  
  49.             @Override  
  50.             public void onClick(View v) {  
  51.                 try {  
  52.                     String xml = parser.serialize(books);  //序列化  
  53.                     FileOutputStream fos = openFileOutput("books.xml", Context.MODE_PRIVATE);  
  54.                     fos.write(xml.getBytes("UTF-8"));  
  55.                 } catch (Exception e) {  
  56.                     Log.e(TAG, e.getMessage());  
  57.                 }  
  58.             }  
  59.         });  
  60.     }  
  61. }  
界面就两个按钮,顺便给大家贴上:

 

点击“readXML”按钮,将会调用SAX解析器解析文档,并在日志台打印相关信息:

然后再点击“writeXML”按钮,将会在该应用包下的files目录生成一个books.xml文件:

使用DOM解析器:

DomBookParser.java代码如下:

[java] view plain copy
  1. package com.scott.xml.parser;  
  2.   
  3. import java.io.InputStream;  
  4. import java.io.StringWriter;  
  5. import java.util.ArrayList;  
  6. import java.util.List;  
  7.   
  8. import javax.xml.parsers.DocumentBuilder;  
  9. import javax.xml.parsers.DocumentBuilderFactory;  
  10. import javax.xml.transform.OutputKeys;  
  11. import javax.xml.transform.Result;  
  12. import javax.xml.transform.Source;  
  13. import javax.xml.transform.Transformer;  
  14. import javax.xml.transform.TransformerFactory;  
  15. import javax.xml.transform.dom.DOMSource;  
  16. import javax.xml.transform.stream.StreamResult;  
  17.   
  18. import org.w3c.dom.Document;  
  19. import org.w3c.dom.Element;  
  20. import org.w3c.dom.Node;  
  21. import org.w3c.dom.NodeList;  
  22.   
  23. import com.scott.xml.model.Book;  
  24.   
  25. public class DomBookParser implements BookParser {  
  26.   
  27.     @Override  
  28.     public List<Book> parse(InputStream is) throws Exception {  
  29.         List<Book> books = new ArrayList<Book>();  
  30.         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  //取得DocumentBuilderFactory实例  
  31.         DocumentBuilder builder = factory.newDocumentBuilder(); //从factory获取DocumentBuilder实例  
  32.         Document doc = builder.parse(is);   //解析输入流 得到Document实例  
  33.         Element rootElement = doc.getDocumentElement();  
  34.         NodeList items = rootElement.getElementsByTagName("book");  
  35.         for (int i = 0; i < items.getLength(); i++) {  
  36.             Book book = new Book();  
  37.             Node item = items.item(i);  
  38.             NodeList properties = item.getChildNodes();  
  39.             for (int j = 0; j < properties.getLength(); j++) {  
  40.                 Node property = properties.item(j);  
  41.                 String nodeName = property.getNodeName();  
  42.                 if (nodeName.equals("id")) {  
  43.                     book.setId(Integer.parseInt(property.getFirstChild().getNodeValue()));  
  44.                 } else if (nodeName.equals("name")) {  
  45.                     book.setName(property.getFirstChild().getNodeValue());  
  46.                 } else if (nodeName.equals("price")) {  
  47.                     book.setPrice(Float.parseFloat(property.getFirstChild().getNodeValue()));  
  48.                 }  
  49.             }  
  50.             books.add(book);  
  51.         }  
  52.         return books;  
  53.     }  
  54.   
  55.     @Override  
  56.     public String serialize(List<Book> books) throws Exception {  
  57.         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  
  58.         DocumentBuilder builder = factory.newDocumentBuilder();  
  59.         Document doc = builder.newDocument();   //由builder创建新文档  
  60.   
  61.         Element rootElement = doc.createElement("books");  
  62.   
  63.         for (Book book : books) {  
  64.             Element bookElement = doc.createElement("book");  
  65.             bookElement.setAttribute("id", book.getId() + "");  
  66.   
  67.             Element nameElement = doc.createElement("name");  
  68.             nameElement.setTextContent(book.getName());  
  69.             bookElement.appendChild(nameElement);  
  70.   
  71.             Element priceElement = doc.createElement("price");  
  72.             priceElement.setTextContent(book.getPrice() + "");  
  73.             bookElement.appendChild(priceElement);  
  74.   
  75.             rootElement.appendChild(bookElement);  
  76.         }  
  77.   
  78.         doc.appendChild(rootElement);  
  79.   
  80.         TransformerFactory transFactory = TransformerFactory.newInstance();//取得TransformerFactory实例  
  81.         Transformer transformer = transFactory.newTransformer();    //从transFactory获取Transformer实例  
  82.         transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");            // 设置输出采用的编码方式  
  83.         transformer.setOutputProperty(OutputKeys.INDENT, "yes");                // 是否自动添加额外的空白  
  84.         transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");   // 是否忽略XML声明  
  85.   
  86.         StringWriter writer = new StringWriter();  
  87.   
  88.         Source source = new DOMSource(doc); //表明文档来源是doc  
  89.         Result result = new StreamResult(writer);//表明目标结果为writer  
  90.         transformer.transform(source, result);  //开始转换  
  91.   
  92.         return writer.toString();  
  93.     }  
  94.   
  95. }  
然后再MainActivity中只需改一个地方:

[java] view plain copy
  1. readBtn.setOnClickListener(new View.OnClickListener() {  
  2.     @Override  
  3.     public void onClick(View v) {  
  4.         try {  
  5.             InputStream is = getAssets().open("books.xml");  
  6. //          parser = new SaxBookParser();  
  7.             parser = new DomBookParser();  
  8.             books = parser.parse(is);  
  9.             for (Book book : books) {  
  10.                 Log.i(TAG, book.toString());  
  11.             }  
  12.         } catch (Exception e) {  
  13.             Log.e(TAG, e.getMessage());  
  14.         }  
  15.     }  
  16. });  

执行结果是一样的。

使用PULL解析器:

PullBookParser.java代码如下:

[java] view plain copy
  1. package com.scott.xml.parser;  
  2.   
  3. import java.io.InputStream;  
  4. import java.io.StringWriter;  
  5. import java.util.ArrayList;  
  6. import java.util.List;  
  7.   
  8. import org.xmlpull.v1.XmlPullParser;  
  9. import org.xmlpull.v1.XmlSerializer;  
  10.   
  11. import android.util.Xml;  
  12.   
  13. import com.scott.xml.model.Book;  
  14.   
  15. public class PullBookParser implements BookParser {  
  16.   
  17.     @Override  
  18.     public List<Book> parse(InputStream is) throws Exception {  
  19.         List<Book> books = null;  
  20.         Book book = null;  
  21.   
  22. //      XmlPullParserFactory factory = XmlPullParserFactory.newInstance();  
  23. //      XmlPullParser parser = factory.newPullParser();  
  24.   
  25.         XmlPullParser parser = Xml.newPullParser(); //由android.util.Xml创建一个XmlPullParser实例  
  26.         parser.setInput(is, "UTF-8");               //设置输入流 并指明编码方式  
  27.   
  28.         int eventType = parser.getEventType();  
  29.         while (eventType != XmlPullParser.END_DOCUMENT) {  
  30.             switch (eventType) {  
  31.                 case XmlPullParser.START_DOCUMENT:  
  32.                     books = new ArrayList<Book>();  
  33.                     break;  
  34.                 case XmlPullParser.START_TAG:  
  35.                     if (parser.getName().equals("book")) {  
  36.                         book = new Book();  
  37.                     } else if (parser.getName().equals("id")) {  
  38.                         eventType = parser.next();  
  39.                         book.setId(Integer.parseInt(parser.getText()));  
  40.                     } else if (parser.getName().equals("name")) {  
  41.                         eventType = parser.next();  
  42.                         book.setName(parser.getText());  
  43.                     } else if (parser.getName().equals("price")) {  
  44.                         eventType = parser.next();  
  45.                         book.setPrice(Float.parseFloat(parser.getText()));  
  46.                     }  
  47.                     break;  
  48.                 case XmlPullParser.END_TAG:  
  49.                     if (parser.getName().equals("book")) {  
  50.                         books.add(book);  
  51.                         book = null;  
  52.                     }  
  53.                     break;  
  54.             }  
  55.             eventType = parser.next();  
  56.         }  
  57.         return books;  
  58.     }  
  59.   
  60.     @Override  
  61.     public String serialize(List<Book> books) throws Exception {  
  62. //      XmlPullParserFactory factory = XmlPullParserFactory.newInstance();  
  63. //      XmlSerializer serializer = factory.newSerializer();  
  64.   
  65.         XmlSerializer serializer = Xml.newSerializer(); //由android.util.Xml创建一个XmlSerializer实例  
  66.         StringWriter writer = new StringWriter();  
  67.         serializer.setOutput(writer);   //设置输出方向为writer  
  68.         serializer.startDocument("UTF-8"true);  
  69.         serializer.startTag("""books");  
  70.         for (Book book : books) {  
  71.             serializer.startTag("""book");  
  72.             serializer.attribute("""id", book.getId() + "");  
  73.   
  74.             serializer.startTag("""name");  
  75.             serializer.text(book.getName());  
  76.             serializer.endTag("""name");  
  77.   
  78.             serializer.startTag("""price");  
  79.             serializer.text(book.getPrice() + "");  
  80.             serializer.endTag("""price");  
  81.   
  82.             serializer.endTag("""book");  
  83.         }  
  84.         serializer.endTag("""books");  
  85.         serializer.endDocument();  
  86.   
  87.         return writer.toString();  
  88.     }  
  89. }  
然后再对MainActivity做以下更改:

[java] view plain copy
  1. readBtn.setOnClickListener(new View.OnClickListener() {  
  2.     @Override  
  3.     public void onClick(View v) {  
  4.         try {  
  5.             InputStream is = getAssets().open("books.xml");  
  6. //          parser = new SaxBookParser();  
  7. //          parser = new DomBookParser();  
  8.             parser = new PullBookParser();  
  9.             books = parser.parse(is);  
  10.             for (Book book : books) {  
  11.                 Log.i(TAG, book.toString());  
  12.             }  
  13.         } catch (Exception e) {  
  14.             Log.e(TAG, e.getMessage());  
  15.         }  
  16.     }  
  17. });  
和其他两个执行结果都一样。

对于这三种解析器各有优点,我个人比较倾向于PULL解析器,因为SAX解析器操作起来太笨重,DOM不适合文档较大,内存较小的场景,唯有PULL轻巧灵活,速度快,占用内存小,使用非常顺手。读者也可以根据自己的喜好选择相应的解析技术。



0 0
原创粉丝点击