对XML文件的重新认识,说说XML文件的二三事(三):XML两类解析模式(1)-SAX解析XML

XML的解析方式常见的有两种,一种是以事件为驱动的XML解析,比如SAX。另外一种是类似于树形属性结构的数据,比如DOM类以及在此基础上延伸升级的JDOM以及DOM4J。这两种解析XML的方式完全不同,是两种不同的机制,其主要对比可以见下表:

XML解析方法

优点

缺点

SAX

1.    效率和性能较好,不需要读入全部数据。

2.    可以根据需求适时停止,相对灵活。

1.    需要程序员自己编写与XML文件相关标签的处理逻辑,

2.    当XML文件较为复杂的时候,代码逻辑比较繁琐。

3.    无法修改XML文件,只能读取。

DOM类

1.    允许对XML数据结构进行更改。

2.    大量使用集合类,方便开发。

1.要把整个XML文件读入内存,导致资源消耗较大。

一.SAX解析器简介

所谓SAX是(Simple API for XML),是一种事件驱动型的XML解析器,是一种边遍历边解析,自上而下依次解析的方法,即先遍历父标签,接着递归遍历子标签,直到子标签不存在。

SAX的解析步骤可以分为如下四步:

1.     工厂方法初始化解析器Parser。

2.     XML文件转换成流数据Stream。

3.     自定义Handler的初始化。

4.     解析器Parser利用自定义Handler对Stream进行处理。

SAX解析器中最重要的一点是自定义Handler的初始化,其内部逻辑是整个SAX解析器的核心所在。正如表格所说,我们需要根据XML文件的内容来编写相关标签的处理逻辑,而自定义Handler的代码逻辑编写由要XML来确定。举例说明:

1.      一个待解析的XML文件User.XML如下所示:

<?xmlversion="1.0"encoding="UTF-8"?>

<users>

 <userid="1">

  <name>小红</name>

  <age>19</age>

 

 </user>

  <userid="2">

     <name>小明</name>

     <age>17</age>

  </user>

  <userid ="3">

  <name>小黄</name>

  <age>18</age>

  </user>

</users>

2.相对应的根据XML文件的内容我们需要定义一个JavaBean,来和XML文件标签内容一一对应,如下所示:

 

public classUser {

private intid;

private Stringname;

private intage;

 

public intgetId() {

  return id;

}

public voidsetId(intid) {

  this.id = id;

}

public String getName() {

  return name;

}

public voidsetName(String name) {

  this.name = name;

}

public intgetAge() {

  return age;

}

public voidsetAge(intage) {

  this.age = age;

}

 

}

3.根据JavaBean和XML文件内逻辑,我们自定义一个继承自DefaultHandler类的Handler,如下所示:

packageutils;

 

importjava.util.ArrayList;

importjava.util.List;

 

importorg.xml.sax.Attributes;

importorg.xml.sax.SAXException;

importorg.xml.sax.helpers.DefaultHandler;

 

publicclass UserParserHandler extends DefaultHandler {

//用于标记标签名字

private String tagName;

private User user;

//定义一个User类的列表

private List<User> userList;

 

public String getTagName() {

    return tagName;

}

 

public void setTagName(String tagName) {

    this.tagName = tagName;

}

 

public User getUser() {

    return user;

}

 

public void setUser(User user) {

    this.user = user;

}

 

public List<User> getUserList() {

    return userList;

}

 

public void setUserList(List<User>userList) {

    this.userList = userList;

}

 

@Override

public void startDocument() throws SAXException{

    //该方法只调用了一次

    this.userList = new ArrayList<User>();

    System.out.println("startDocument方法只调用了一次");

}

 

@Override

public void endDocument() throws SAXException {

    // TODO Auto-generated method stub

    // 该方法值只调用一次

    System.out.println("endDocument方法只调用了一次");

}

 

/*@purpose 标签(元素)解析的时候调用

 * @paramuri:XML文件的命名空间

 * @paramlocalName:标签的名字

 * @paramqName:带有命名空间的标签的名字

 * @paramattributes:标签的属性集

 * @logic:如果标签名和目标标签名字相同,则把属性集给加入到Id中。

 * 然后把当前处理的标签给标记出来

 * */

@Override

public void startElement(String uri, StringlocalName, String qName,

      Attributes attributes) throws SAXException{

    // TODO Auto-generated method stub

    // super.startElement(uri, localName, qName,attributes);

    if (qName.equals("user")) {

      user = new User();

 

      for (int i = 0; i <attributes.getLength(); i++) {

      System.out.println(attributes.getLocalName(i)+":"

             + attributes.getValue(i));

       

        if("id".equals(attributes.getLocalName(i))) {

        user.setId(Integer.parseInt(attributes.getValue(i)));

        }

 

      }

    }

    this.tagName = qName; // 把当前标签记录下来,用来解析特征元素用的

}

 

@Override

public void endElement(String uri, StringlocalName, String qName)

      throws SAXException {

    // TODO Auto-generated method stub

    super.endElement(uri, localName, qName);

    if (qName.equals("user")) {

      this.userList.add(this.user);

 

    }

    this.tagName = null;

}

/*@purpose:解析标签内容的时候调用

 * @param ch:读取到的文本节点的字节数组

 * @param start:字节开始的位置,为0则表示开始

 * @param length:字节数组的长度

 * @logic:验证标签是否为空,接着开始针对不同的标签设置Java Bean的属性值

 *

 * */

@Override

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

      throws SAXException {

    // TODO Auto-generated method stub

    // super.characters(ch, start, length);

    if (this.tagName != null) {

      String value = new String(ch, start,length); // 将当前TextNode转换为String

      if (this.tagName.equals("name"))

        user.setName(value);

      else if (this.tagName.equals("age"))

        user.setAge(Integer.parseInt(value));

 

    }

}

 

}

1.正如上述代码所示:代码继承了DefaultHandler,并重写了5个函数,分别为:

1)   StartDocment,整个XML遍历解析过程中,该方法只调用一次,可以用来对User类的集合进行初始化。

2)  EndDocument,该方法也仅仅调用了一次,并没有多少实际的意义。

3)  StartElement,该方法是整个SAX解析核心的开始,其主要逻辑是对父标签进行解析,并对User类变量进行初始化。然后对该标签进行标注。

4)  Characters,该方法是紧跟着StartElement方法的,其作用是对相应子标签进行解析,并把结果赋值到User类中。

5)  EndElement,该方法是把解析赋值后的User类变量添加到User类列表List中,并对解析的标签进行还原标注。

上述五个方法的执行逻辑如下:

startDocument-startElement-characters-endElement-endDocument

具体可以描述为:

1.    XML文件开始解析的时候,启动startDocument函数,该函数初始化User对象列表并在整个过程中只被调用了一次。

2.按照上述流程走到characters的时候,此时如果该节点无子标签节点,则一遍遍历通过到endDocument,该节点标签的解析完成。如果该标签节点存在子节点标签,那么需要返回到startElement,即回调该方法。然后走完余下的流程。

3.整个XML的解析过程是一个回调递归的过程。

4.代码测试,完全按照上述四个解析步骤进行测试:

总结:SAX解析器是一种事件驱动的回调递归式的解析,当XML文件的逻辑相对简单,或者保存的为数据需要读取出来的时候,建议选择SAX解析器。

猜你喜欢

转载自blog.csdn.net/horero/article/details/76735597