zoukankan      html  css  js  c++  java
  • 利用sax处理xml(推荐)

    转自:http://www.eoeandroid.com/thread-33212-1-1.html

    相信各位android开发者,对SAX已经并不陌生了,SAX(Simple API for XML),是一个使用非常广泛的XML解析标准,通常使用Handler模式来处理XML文档,这种处理模式和我们平常习惯的理解方式很不同,身边也经常有一些朋友在刚接触SAX的时候会觉得理解起来有些困难。其实SAX并不复杂,只不过是换了一种思维方式,正如它的名字所表示的,为了让我们以更简单的方式来处理XML文档,下面我们就开始吧。

          我们通常的理解方式是,我们给出一个输入(比如xml文档的地址),然后程序返回给我们数据(比如解析后的xml文档结构),我们在返回给我们的结果中进行相应的操作,而SAX以一种更简单的方式来处理XML文档的解析,也就是处理器模式,一个使用SAX的简单示例:

    1 SAXParserFactory spf = SAXParserFactory.newInstance();
    2   SAXParser sp = spf.newSAXParser();
    3   XMLReader reader = sp.getXMLReader();
    4 
    5   
    6   reader.setContentHandler(myHandler);            
    7   reader.parse(new InputSource(new URL(url).openStream()));

         正如上面的代码,我们使用一系列工厂方法生成了一个XMLReader对象,随后,最关键的一行就是reader.setContentHandler,这里为这个reader设置了一个处理器,这个处理器的具体内容是要我们来完成的,稍后会详细介绍,最后调用parse方法完成文档的解析。这是SAX的一个基本流程。

         下面我们来详细介绍一下处理器,SAX处理器使用的是一种和我们平时的理解方式不太一样的处理形式,是在遍历文档的同时,让我们来进行文档的处理。
         用一个实际的例子来解释更为方便,假如有下面这样一个XML文档:

     1 <student>
     2   <name>张三</name>
     3   <age>22</age>
     4   <sn>1001</sn>
     5 </student>
     6 <student>
     7   <name>李四</name>
     8   <age>21</age>
     9   <sn>1002</sn>
    10 </student>

          使用SAX的时候,解析器会对XML文档进行深度优先遍历,在遍历的时候,会根据条件调用处理器中的方法,如上面的XML文档,首先会遍历到第一个student的起始节点,这时我们可以在处理器中进行一些需要的处理,随后会分别遍历name,age,sn起始节点和结束节点,以此类推,这样说起来可能还不够直观,下面我们就来看看一个处理器的基本结构

     1 public class MyHandler extends DefaultHandler {
     2     
     3     public void startElement(String uri, String localName, String qName,    
     4     }
     5     
     6     public void endElement(String uri, String localName, String qName)
     7             throws SAXException {    
     8     }
     9     
    10     public void characters(char[] ch, int start, int length)
    11             throws SAXException {
    12     }  
    13 }


          如上面的代码,这里有几个比较重要的方法,startElement是进入到起始节点的时候会调用的方法,例如上面的xml文件,进入到<student>节点时,就会调用startElement方法。
         endElement方法,在结束一个节点的时候会调用,例如进入到</student>节点时,该方法会被调用。
         characters方法,在进入XML节点的文本节点(TextNode)时会被调用,例如<name>张三</name>,在便利到‘张三’这个文本节点的时候,这个方法会被调用。

         另外还有两个回调方法,分别为startDocument,endDocument,顾名思义,这两个方法为进入文档和离开文档时要调用的方法。


         下面我们就来自己写一个处理器来解析上面的XML文档。首先我们需要将每个节点封装成一个实体对象:

     
     1 public class Student {
     2     private String name;
     3     
     4     private int age;
     5     
     6     private String sn;
     7     public String getName() {
     8         return name;
     9     }
    10     public void setName(String name) {
    11         this.name = name;
    12     }
    13     public int getAge() {
    14         return age;
    15     }
    16     public void setAge(int age) {
    17         this.age = age;
    18     }
    19     public String getSn() {
    20         return sn;
    21     }
    22     public void setSn(String sn) {
    23         this.sn = sn;
    24     }
    25     
    26     
    27 }

         下面再来完成处理器的代码:

     1 public class MyHandler extends DefaultHandler {
     2     
     3     private List<Student> studentList;
     4     
     5     private boolean inStudent = false; 
     6     
     7     private boolean studentName = false;
     8     
     9     private boolean studentAge = false;
    10     
    11     private boolean studentSN = false;
    12     
    13     private Student curStudent ;
    14     
    15     public MyHandler() {
    16         
    17         studentList = new ArrayList<Student>();
    18     }
    19     @Override
    20     public void startElement(String uri, String localName, String qName,
    21             Attributes attributes) throws SAXException {
    22         
    23         String tagName = localName.length() != 0 ? localName : qName;
    24         tagName = tagName.toLowerCase().trim();
    25         
    26         if(tagName.equals("student")) {
    27             inStudent  = true;
    28             curStudent = new Student();
    29         }
    30         
    31         if(inStudent) {
    32             
    33             if(tagName.equals("name")) {
    34                 studentName = true;
    35             }else if(tagName.equals("age")) {
    36                 studentAge = true;
    37             }else if(tagName.equals("sn")) {
    38                 studentSN = true;
    39             }
    40         }
    41         
    42     }
    43     
    44     @Override
    45     public void endElement(String uri, String localName, String qName)
    46             throws SAXException {
    47         
    48         String tagName = localName.length() != 0 ? localName : qName;
    49         tagName = tagName.toLowerCase().trim();
    50         
    51         if(tagName.equals("student")) {
    52             inStudent  = true;
    53             studentList.add(curStudent);
    54         }
    55         
    56         if(inStudent) {
    57             
    58             if(tagName.equals("name")) {
    59                 studentName = false;
    60             }else if(tagName.equals("age")) {
    61                 studentAge = false;
    62             }else if(tagName.equals("sn")) {
    63                 studentSN = false;
    64             }
    65         }
    66     }
    67     
    68     @Override
    69     public void characters(char[] ch, int start, int length)
    70             throws SAXException {
    71         
    72         if(studentName) {
    73             curStudent.setName(curStudent.getName() + new String(ch,start,length));
    74         }else if (studentAge) {
    75             curStudent.setAge(Integer.parseInt(new String(ch,start,length)));
    76         }else if(studentSN) {
    77             curStudent.setSn(curStudent.getSn() + new String(ch, start, length));            
    78         }
    79     }
    80 }

        如上面的代码,我们使用了一系列的布尔标志变量来保存文档的遍历状态,先从startElement说起,当我们进入到student节点的时候,我们将inStudent状态设置为true,表示我们已经处于student节点之中,同时创建了一个student对象,相应地,在endElement方法中,我们遇到student结束的时候,会把这个对象添加到我们的studentList中,并将inStudent状态设置为false。同样的,在startElement方法中判断instudent状态,如果当前已经处于student节点中,并且遍历到name,age或者sn节点时,我们也将相应的标志设置为true。这样在遍历的文本节点的时候就可以在characters方法中通过判断这些标志位来为Student对象设置相应的属性。

        注意到,这里curStudent.setName(curStudent.getName() + new String(ch,start,length)),我们用以前的值和新的值连接起来,而不是直接设置curStudent.setName(new String(ch,start,length))。这是因为在遍历<name>.....</name>这中间的文本节点的时候,有些时候这对标签中的内容可能会被看做多个文本节点,比如包含Html实体的情况下 <name>张&nbsp;三</name>,这里相当于包含了两个文本节点,如果不使用连接的方式而采用直接设置的方式,那么我们最终只能得到最后一次设置的值,因为前面设置的被覆盖了。那么我们最终取得到的名字就是‘三’了。

        这个处理器的核心分功能就算完成了,下面我们还需要增加一个方法,用来返回处理后的内容:   

    public List<Student> getStudentList() {
            return studentList;
        }



       完成了处理器之后,我们就可以用刚开始介绍的方法来解析XML文档了:

    1 SAXParserFactory spf = SAXParserFactory.newInstance();
    2   SAXParser sp = spf.newSAXParser();
    3   XMLReader reader = sp.getXMLReader();
    4 
    5   List<Student> list;
    6   reader.setContentHandler(myHandler);            
    7   reader.parse(new InputSource(new URL(url).openStream()));
    8 
    9 list = myHandler.getStudentList();

     

         可以看到,解析完XML文档之后,我们就可以用处理器重的getStudentList方法取得解析后的数据了。

         最后总结一下,SAX并不复杂,只要理解了它的思维方式,我们就可以游刃有余,使它成为我们开发的利器,这篇文章向大家介绍了SAX的一些基本知识,希望能起到一个抛砖引玉的作用,大家能够使用它来创造出更多好的应用,当然可能有一些地方解释的还不是十分完美,如果有一些不好理解的地方,还望大家指出。:lol

         另外下面是SAX的一个官方网站,里面有一些介绍和代码示例,英文不错的童鞋可以来这里参考一下:loveliness:
         http://www.saxproject.org/

  • 相关阅读:
    shell 重启 tomcat 脚本
    shell 复制/备份文件 脚本
    在 CentOS 上安装 node.js
    架构漫谈(一):什么是架构? -王概凯
    冷静审视人工智能技术的本质 | 一图看懂新一代人工智能知识体系大全
    时代在变
    什么是设计思维Design Thinking——风靡全球的创造力培养方法
    金融即服务(FaaS),将开启场景化金融新格局
    devops工具
    京东金融-供应链金融业务介绍
  • 原文地址:https://www.cnblogs.com/qingblog/p/2601078.html
Copyright © 2011-2022 走看看