关于SAX,DOM,PULL,DOM4J解析XML文件的优劣势浅显分析

来源:互联网 发布:trusty ubuntu 编辑:程序博客网 时间:2024/05/10 07:26

关于SAX,DOM,PULL,DOM4J解析XML文件的优劣势浅显分析

 

1.SAX简介

 

SAX处理的优点非常类似于流媒体的优点。分析能够立即开始,而不是等待所有的数据被处理。而且,由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上,应用程序甚至不必解析整个文档;它可以在某个条件得到满足时停止解析。一般来说,SAX还比它的替代者DOM快许多。 

选择DOM还是选择SAX? 对于需要自己编写代码来处理XML文档的开发人员来说, 选择DOM还是SAX解析模型是一个非常重要的设计决策。 DOM采用建立树形结构的方式访问XML文档,而SAX采用的事件模型。 

    DOM解析器把XML文档转化为一个包含其内容的树,并可以对树进行遍历。用DOM解析模型的优点是编程容易,开发人员只需要调用建树的指令,然后利用navigation APIs访问所需的树节点来完成任务。可以很容易的添加和修改树中的元素。然而由于使用DOM解析器的时候需要处理整个XML文档,所以对性能和内存的要求比较高,尤其是遇到很大的XML文件的时候。由于它的遍历能力,DOM解析器常用于XML文档需要频繁的改变的服务中。 

    SAX解析器采用了基于事件的模型,它在解析XML文档的时候可以触发一系列的事件,当发现给定的tag的时候,它可以激活一个回调方法,告诉该方法制定的标签已经找到。SAX对内存的要求通常会比较低,因为它让开发人员自己来决定所要处理的tag.特别是当开发人员只需要处理文档中所包含的部分数据时,SAX这种扩展能力得到了更好的体现。但用SAX解析器的时候编码工作会比较困难,而且很难同时访问同一个文档中的多处不同数据。 

 

1.1 SAX语法

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提供实现ContentHandler接口的类,那么该类就可以得到通知事件(实际上就是SAX调用了该类中的回调方法)。因为ContentHandler是一个接口,在使用的时候可能会有些不方便,因此,SAX还为其制定了一个Helper类:DefaultHandler,它实现了ContentHandler接口,但是其所有的方法体都为空,在实现的时候,你只需要继承这个类,然后重写相应的方法即可。

 

1.2 SAX解析XMl的例子

文件名称:itcast.xml

<?xml version="1.0" encoding="UTF-8"?>

<persons>

<person id="23">

<name>zhangsan</name>

<age>30</age>

</person>

<person id="20">

<name>lisi</name>

<age>25</age>

</person>

</persons>

    在该XML文档中有2种节点:element节点和text节点

 

使用SAX解析itcast.xml的代码如下:

public static List<Person> readXML(InputStream inStream) {

   try {

SAXParserFactory spf = SAXParserFactory.newInstance();

SAXParser saxParser = spf.newSAXParser(); //创建解析器

//设置解析器的相关特性,http://xml.org/sax/features/namespaces = true 表示开启命名空间特性  

//saxParser.setProperty("http://xml.org/sax/features/namespaces",true);

XMLContentHandler handler = new XMLContentHandler();

saxParser.parse(inStream, handler);

inStream.close();

return handler.getPersons();

   } catch (Exception e) {

e.printStackTrace();

   }

  return null;

}

注:SAX 支持已内置到JDK1.5中,你无需添加任何的jar文件。

 

XMLContentHandler的实现如下:

import java.util.List;

 

import org.xml.sax.Attributes;

import org.xml.sax.SAXException;

import org.xml.sax.helpers.DefaultHandler;

 

import cn.itcast.xml.domain.Person;

 

public class XMLContentHandler extends DefaultHandler {

private List<Person> persons = null;

private Person currentPerson;

private String tagName = null;//当前解析的元素标签

 

public List<Person> getPersons() {

return persons;

}

/*

 * 接收文档的开始的通知。

 */

@Override public void startDocument() throws SAXException {

persons = new ArrayList<Person>();

}

/*

 * 接收字符数据的通知。

 */

@Override public void characters(char[] ch, int start, int length) throws SAXException {

if(tagName!=null){

String data = new String(ch, start, length);

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

this.currentPerson.setName(data);

}else if(tagName.equals("age")){

this.currentPerson.setAge(Short.parseShort(data));

}

}

}

/*

 * 接收元素开始的通知。

 * 参数意义如下:

 *    namespaceURI:元素的命名空间

 *    localName :元素的本地名称(不带前缀)

 *    qName :元素的限定名(带前缀)

 *    atts :元素的属性集合

 */

@Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {

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

currentPerson = new Person();

currentPerson.setId(Integer.parseInt(atts.getValue("id")));

}

this.tagName = qName;

}

/*

 * 接收文档的结尾的通知。

 * 参数意义如下:

 *    uri :元素的命名空间

 *    localName :元素的本地名称(不带前缀)

 *    qName :元素的限定名(带前缀)

 * 

 */

@Override

 public void endElement(String uri, String localName, String qName) throws SAXException {

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

persons.add(currentPerson);

currentPerson = null;

}

this.tagName = null;

}

}

 

完整代码如下:

/**

 * 采用SAX技术读取XML文件

 * @param xml

 * @return

 * @throws Exception

 */

public  List<Person> getPersons(InputStream xml) throws Exception {

SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();

SAXParser saxParser = saxParserFactory.newSAXParser();

PersonParser personParser = new PersonParser();

saxParser.parse(xml, personParser);

xml.close();

return personParser.getPersons();

}

 

    private final class PersonParser extends DefaultHandler {

     private List<Person> persons = null;

     public List<Person> getPersons() {

return persons;

}

private String tag = null;

     private Person person = null;

public void startDocument() throws SAXException {

persons = new ArrayList<Person>();

}

 

//元素开始事件

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

Attributes attributes) throws SAXException {

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

person = new Person();

person.setId(Integer.parseInt(attributes.getValue(0)));

}

tag = qName;

}

 

//元素结束事件

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

throws SAXException {

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

persons.add(person);

person = null;

}

tag = null;

}

 

//获取text节点的事件

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

throws SAXException {

if (tag != null){

//将该字符数组中的值存放到字符串变量中

String data = new String(ch,start,length);

//该节点的上一个节点的名字

if ("name".equals(tag)){

person.setName(data);

}else if ("age".equals(tag)){

person.setAge(Integer.parseInt(data));

}

}

}

}

 

 

 

2. DOM简介

除了使用 SAX可以解析XML文件,大家也可以使用熟悉的DOM来解析XML文件。 DOM解析XML文件时,会将XML文件的所有内容以文档树方式存放在内存中,然后允许您使用DOM API遍历XML树、检索所需的数据。使用DOM操作XML的代码看起来是比较直观的,并且在编码方面比基于SAX的实现更加简单。但是,因为DOM需要将XML文件的所有内容以文档树方式存放在内存中,所以内存的消耗比较大,特别对于运行Android的移动设备来说,因为设备的资源比较宝贵,所以建议还是采用SAX来解析XML文件,当然,如果XML文件的内容比较小采用DOM也是可行的。

 

2.1 DOM解析XML的例子

    /**

 * 采用DOM读取XML文件

 * @param xml

 * @return

 * @throws Exception

 */

public  List<Person> getPersons(InputStream xml) throws Exception {

List<Person> persons = new ArrayList<Person>();

        //获取文档创建者工厂的实例

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

//文档创建者实例

DocumentBuilder documentBuilder = dbf.newDocumentBuilder();

//利用文档创建者将xml文件中的内容解析成Document对象保存起来

Document document =  documentBuilder.parse(xml); 

//遍历每个person节点

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

Person person =  new Person();

//获取第iperson节点

Element personElement = (Element)personNodes.item(i);

person.setId(Integer.parseInt(personElement.getAttribute("id")));

//获取当前person节点的子节点的集合(5个)

NodeList personChilds = personElement.getChildNodes(); 

//遍历每个子节点

for(int v=0; v<personChilds.getLength(); v++){

//判断当前person节点的第i个子节点的类型是否为元素节点

if (personChilds.item(v).getNodeType() == Node.ELEMENT_NODE){

//获取person元素的第i个子节点

Node node =  (Node) personChilds.item(v);

Element personChild = (Element)node;

//获取该元素的名字

String nodeName = personChild.getNodeName();

if ("name".equals(nodeName)){

//获取name元素的第一个子节点的值(text节点的值)

    person.setName(personChild.getFirstChild().getNodeValue());  

}else if ("age".equals(nodeName)){

                   person.setAge

(Integer.parseInt(personChild.getFirstChild().getNodeValue()));

}

}

}

persons.add(person);

}

return persons;

}

}

 

 

3.Pull解析器简介

除了可以使用 SAXDOM解析XML文件,大家也可以使用Android内置的Pull解析器解析XML文件。 Pull解析器的运行方式与 SAX 解析器相似。它提供了类似的事件,如:开始元素和结束元素事件,使用parser.next()可以进入下一个元素并触发相应事件。事件将作为数值代码被发送,因此可以使用一个switch对感兴趣的事件进行处理。当元素开始解析时,调用parser.nextText()方法可以获取下一个Text类型节点的值。

 

3.1使用Pull解析器读取XML文件

/**

使用Pull解析器读取XML文件

* @param xml

 * @return

 * @throws Exception

 */

@SuppressWarnings("static-access")

public static List<Person> getPersons(InputStream xml) throws Exception {

List<Person> persons = null;

Person person = null;

XmlPullParser pullParser = Xml.newPullParser();

pullParser.setInput(xml, "UTF-8");

//产生第一个事件

int event = pullParser.getEventType();  

//只要不是文档结束事件

while (event != pullParser.END_DOCUMENT) { 

switch (event) {

//文档开始事件,可以进行数据初始化处理

case XmlPullParser.START_DOCUMENT:

persons = new ArrayList<Person>();

break;

//开始元素事件

case XmlPullParser.START_TAG:

if ("person".equals(pullParser.getName())) {

//获取当前解析器指向的节点的属性值

int id = Integer.parseInt(pullParser.getAttributeValue(0));

person = new Person(0, null, 0);

person.setId(id);

}

if ("name".equals(pullParser.getName())) {

//当前解析器指向的下一个节点的值

String name = pullParser.nextText();

person.setName(name);

}

if ("age".equals(pullParser.getName())) {

int age = Integer.parseInt(pullParser.nextText());

person.setAge(age);

}

break;

    //触发结束元素事件时,什么都不做

case XmlPullParser.END_TAG:

//如果当前解析器指向 的节点的名字为person,则表示一个person节点结束

if ("person".equals(pullParser.getName())) {

persons.add(person);

person = null;

}

break;

}

//让当前的解析器进入到一下个元素,并触发相应的事件

event = pullParser.next();  

}

return persons;

}

 

3.2使用Pull解析器生成XML文件

    /**

     * 使用Pull解析器生成XML文件

     * @param persons

     * @param os

     * @throws Exception

     */

public static void save(List<Person> persons, OutputStream os)

throws Exception {

XmlSerializer xmlSerializer = Xml.newSerializer();

xmlSerializer.setOutput(os, "UTF-8");

//文档开始

xmlSerializer.startDocument("UTF-8", true);

//persons节点开始

xmlSerializer.startTag(null, "persons");

//循环生成每一个person节点内容

for (Person person : persons) {

    //person节点开始

xmlSerializer.startTag(null, "person");

   xmlSerializer.attribute(null, "id", person.getId().toString());

   //name子节点开始

   xmlSerializer.startTag(null, "name");

       xmlSerializer.text(person.getName());

   xmlSerializer.endTag(null, "name");

   xmlSerializer.startTag(null, "age");

       xmlSerializer.text(person.getAge().toString());

   xmlSerializer.endTag(null, "age");

xmlSerializer.endTag(null, "person");

}

//persons节点结束

xmlSerializer.endTag(null, "persons");

//文档结束

xmlSerializer.endDocument();

os.flush();

os.close();

}

 

 

 

4. DOM4J 简介

 

虽然DOM4J代表了完全独立的开发结果,但最初,它是JDOM的一种智能分支。它合并了许多超出基本XML文档表示的功能,包括集成的XPath支持、XML Schema支持以及用于大文档或流化文档的基于事件的处理。它还提供了构建文档表示的选项,它通过DOM4J API和标准DOM接口具有并行访问功能。从2000下半年开始,它就一直处于开发之中。 

    为支持所有这些功能,DOM4J使用接口和抽象基本类方法。DOM4J大量使用了API中的Collections类,但是在许多情况下,它还提供一些替代方法以允许更好的性能或更直接的编码方法。直接好处是,虽然DOM4J付出了更复杂的API的代价,但是它提供了比JDOM大得多的灵活性。 

在添加灵活性、XPath集成和对大文档处理的目标时,DOM4J的目标与JDOM是一样的:针对Java开发者的易用性和直观操作。它还致力于成为比JDOM更完整的解决方案,实现在本质上处理所有Java/XML问题的目标。在完成该目标时,它比JDOM更少强调防止不正确的应用程序行为。 

    DOM4J是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。如今你可以看到越来越多的Java软件都在使用DOM4J来读写XML,特别值得一提的是连SunJAXM也在用DOM4J. 

 

4.1DOM4J代码

SAXReader reader = new SAXReader(); //

Document document = reader.read(inStream);

Element root = document.getRootElement();

for (Iterator i = root.elementIterator("person"); i.hasNext();) {

Element element = (Element) i.next(); // 获取每个person元素

System.out.println("id=" + element.attributeValue("id"));

System.out.println("name=" + element.elementText("name"));

System.out.println("age=" + element.elementText("age"));

}

String xpath = "//persons/person";

List<Node> nodes = document.selectNodes(xpath);

for (Node n : nodes) {

System.out.println(((Element)n).attributeValue("id"));

System.out.println("name=" + ((Element)n).elementText("name"));

System.out.println("age=" + ((Element)n).elementText("age"));

}

注:需要dom4j-1.6.1.jar 和  jaxen-1.1-beta-6.jar

 

5. SAXDOMpullDOM4J 

 

u DOM4J性能最好,连SunJAXM也在用DOM4J.目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性,那就采用DOM4J. 

u DOM在性能测试时表现不佳,在测试10M文档时内存溢出。在小文档情况下还值得考虑使用DOM。另外,DOM仍是一个非常好的选择。DOM实现广泛应用于多种编程语言。它还是许多其它与XML相关的标准的基础,因为它正式获得W3C推荐(与基于非标准的Java模型相对),所以在某些类型的项目中可能也需要它(如在JavaScript中使用DOM)。 

u SAX表现较好,这要依赖于它特定的解析方式-事件驱动。一个SAX检测即将到来的XML流,但并没有载入到内存(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)。

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 动车晚点导致没赶上下一趟怎么办 浙大三位一体选考分数报错了怎么办 报到证上时间到期了还没报到怎么办 不停的打嗝已经超过24小时怎么办 面对不给下属做主的领导怎么办 法院说退款受伤人去不了证明怎么办 e栈快递柜没收到短信怎么办 京东快递放门卫丢了怎么办 丰巢快递柜收不到取件码怎么办 丰巢快递柜手机号码填错了怎么办 e栈手机号换了取不出来怎么办 买高铁票起点一样终点不一样怎么办 定火车票把名字写错一个字怎么办 火车票坐完车票丢了报销怎么办 客户交给我的钱被骗了怎么办 要证明网络来源的可靠性该怎么办 魅族手机高德地图信号弱怎么办 t恤的印花粘粘的怎么办 从包图网下载的模板素材丢失怎么办 大屏导航的语音功能怎么办用 手机导航不走地图上面走时怎么办 行车撞到步行人死亡师机逃离怎么办 小天才平板电脑不显示了怎么办 高德地图导航不显示车速怎么办 桥梁梁片强度达不到设计要求怎么办 新车交车检验表客户没签字怎么办 中铁快运职工拒绝提货要怎么办 奇瑞a3暖风水箱爆了怎么办 别人挖鱼塘占了我的山土怎么办 自己的鱼塘让别人强行占住了怎么办 公路扩路占地占了鱼塘怎么办? 玉米皮编垫子编好后玉米绳怎么办 入户门门框未预留纱窗位怎么办 门和墙有2cm缝隙怎么办 支座预埋钢板忘记埋了怎么办 做完线雕一边紧一边松怎么办 卖家把没发货的填写了单号怎么办 买的人民币白银亏了好多钱怎么办 带控制线的三相四线开关怎么办 覆膜除尘布袋风拉不动怎么办 家里装修把暖气管道打破了怎么办