XML解析

目前有三种用于Java的流行XML解析技术:

● 文档对象模型(Document Object Model,DOM),这是一个来自W3C的成熟标准;

● 用于XML的简单API(Simple API for XML,SAX),是第一个被广泛采用的用Java编写的XML API,是一个事实上的标准;

● 用于XML的数据流API(Streaming API for XML,StAX),是JSR-173中采用的一个很有前途的新解析模型。

这三种技术各有优缺点,分别适合于不同的Java应用。

DOM解析

当你的应用程序需要不断地导航、修改文档或随机地一次访问整个文档时,使用DOM解析。

DOM是一种基于树型的解析技术,它在内存中构建起一棵完整的解析树,借此实现对整个XML文档的全面、动态访问。

图 1显示了DOM解析模型的树型结构。文档是所有DOM树的根,这个根有至少一个子节点,即根元素,另一个节点是文档类型(DocumentType),用 于DTD说明。根元素有子节点,其子节点还有自己的子节点。子节点可以是元素、文本、注释、处理指令及其他类似信息。

DOM的使用非常简单。你可以随机地访问XML文档,由于整个树都构建在内存中,因此可以通过DOM API修改这些节点,例如增加一个子节点或修改、删除一个节点。

不 过,虽然内存树结构提供了很好的导航支持,但仍有一些解析策略问题需要仔细考虑。首先,整个XML文档必须一次解析完成,不可能只做部分解析;其次,在内 存中加载整个文档和构建完整树结构的成本很高,尤其当文档非常大的时候。典型地,DOM树的容量比文档容量要大一个数量级,所以它要消耗大量内存;第三, 一般的DOM节点类型在互操作性上有优势,但对于对象类型绑定也许不是最好的。

某些 类型的应用程序要比其他类型的应用程序更适合采用DOM解析。例如,当应用程序需要随机访问XML文档时就很适合采用DOM解析,另一个比较好的例子是在 处理模板时需要重复查找整个文件的XSL处理器。此外,由于DOM使你能够更新文档,因此对于需要修改数据的应用程序,如XML编辑器来说,DOM解析也 很方便。

SAX解析

当你需要简单的只读数据流并且希望一个体现成熟标准的强健实施方案时,就使用SAX解析。

SAX是一个用于处理XML事件驱动的“推”模型,虽然它不是W3C标准,但它却是一个得到了广泛认可的API。SAX解析器不像DOM那样建立一个完整的文档树,而是在读取文档时激活一系列事件,这些事件被推给事件处理器,然后由事件处理器提供对文档内容的访问。

常见的事件处理器有三种基本类型:

● 用于访问XML DTD内容的DTDHandler;

● 用于低级访问解析错误的ErrorHandler;

● 用于访问文档内容的ContentHandler,这也是最普遍使用的事件处理器。

图 2显示了SAX解析器如何通过一个回调机制报告事件。解析器读取输入文档并在处理文档时将每个事件推给文档处理器(MyContentHandler)。

与 DOM相比,SAX解析器能提供更好的性能优势,它提供对XML文档内容的有效低级访问。SAX模型最大的优点是内存消耗小,因为整个文档无需一次加载到 内存中,这使SAX解析器可以解析大于系统内存的文档。另外,你无需像在DOM中那样为所有节点创建对象。最后,SAX“推”模型可用于广播环境,能够同 时注册多个ContentHandler,并行接收事件,而不是在一个管道中一个接一个地进行处理。

SAX 的缺点是你必须实现多个事件处理程序以便能够处理所有到来的事件,同时你还必须在应用程序代码中维护这个事件状态,因为SAX解析器不能交流元信息,如 DOM的父/子支持,所以你必须跟踪解析器处在文档层次的哪个位置。如此一来,你的文档越复杂,你的应用逻辑就越复杂。虽然没有必要一次将整个文档加载到 内存中,但SAX解析器仍然需要解析整个文档,这点和DOM一样。

也许SAX面临的最大问题是它没有内置如XPath所提供的那些导航支持。再加上它的单遍解析,使它不能支持随机访问。这一限制也表现在名字空间上: 对有继承名字空间的元素不做注解。这些限制使SAX很少被用于操作或修改文档。

那 些只需要单遍读取内容的应用程序可以从SAX解析中大大受益。很多B2B和EAI应用程序将XML用做封装格式,接收端用这种格式简单地接收所有数据。这 就是SAX明显优于DOM的地方:因高效而获得高吞吐率。在SAX 2.0 中有一个内置的过滤机制,可以很轻松地输出一个文档子集或进行简单的文档转换。

StAX解析

当你要求完整名字空间或多文档支持,或需要一个对象接口时,将StAX用于数据流应用程序。

StAX 是一种令人激动的新解析技术,它与SAX一样,也使用事件驱动模型。但StAX并不使用SAX的“推”模型,而是使用“拉”模型进行事件处理。此 外,StAX解析器也不使用回调机制,而是根据应用程序的要求返回事件。StAX还提供了用户友好的API,用于读入和写出。

StAX与SAX的重要差别之一是SAX向ContentHandler返回不同类型的事件,而StAX却将它的事件返回给应用程序,甚至可以以对象的形式提供事件。

图 3显示了当应用程序要求一个事件时,StAX解析器根据需要从XML文档读取并将该事件返回给该应用程序。

StAX提供了用于创建StAX读写器的工具,所以应用程序可以使用StAX接口而无需参考特定实现的细节。

与DOM和SAX不同,StAX指定了两个解析模型:指针模型,如SAX,它简单地返回事件;迭代程序模型,它以对象形式返回事件,提供更符合习惯的接口,但需要额外的对象创建开销。

StAX 指针模型的性能与SAX解析相当,其差别在于使用StAX应用程序可以控制解析,使得代码更易于编写和维护。StAX还提供了易于使用的迭代程序模型,不 过在这种情况下创建事件对象要付出性能代价。SAX要求应用程序跟踪其在文档中的位置,而StAX则与此不同,由于它具有返回所请求事件的能力,从而使应 用程序无需进行这种跟踪。

另一方面,与DOM相比,StAX有一些与SAX类似的缺点,那就是缺乏全面的导航支持。不过在StAX中,对文档的前向遍历比在SAX中容易,因为应用程序可以控制它获取哪个事件以及获取时间。

StAX修改文档的能力也与SAX相似,因为它们都要创建一个新文档。StAX的指针模型和迭代程序模型提供写出API,但如果你希望不仅仅是单遍转换的话,那么文档的修改还是相当困难。

StAX 解析可满足SAX应用程序的大部分要求,所以如果一个应用程序很适合使用SAX解析,那么它也会从采用StAX中受益。而且,当应用程序为了提高性能而需 要利用数据流模型,同时维持对名字空间的全面支持时,StAX解析是最佳选择。最后,为了处理多个输入,应用程序可以轻松地向多个StAX解析器请求事件 并将它们放入一个单一环境中,而无需启动多个线程。StAX在Web服务和JAX-RPC等新领域尤为有用,在这些领域中,所有这些特性都是必需的。

 

 

 

   选择 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 解析器的时候编码工作会比较困难,而且很难同时访问同一个文档中的多处不同数据。

  Bean文件:

  package com.test;
  import org.xml.sax.*;
  import org.xml.sax.helpers.*;
  import javax.xml.parsers.*;

  public class MyXMLReader extends DefaultHandler {

  java.util.Stack tags = new java.util.Stack();

  public MyXMLReader() {
  super();
  }

  public static void main(String args[]) {
  long lasting = System.currentTimeMillis();
  try {
   SAXParserFactory sf = SAXParserFactory.newInstance();
   SAXParser sp = sf.newSAXParser();
   MyXMLReader reader = new MyXMLReader();
   sp.parse(new InputSource("data_10k.xml"), reader);
  } catch (Exception e) {
   e.printStackTrace();
  }
  System.out.println("运行时间:" + (System.currentTimeMillis() - lasting) + " 毫秒");
  }

  public void characters(char ch[], int start, int length) throws SAXException {
  String tag = (String) tags.peek();
  if (tag.equals("NO")) {
   System.out.print("车牌号码:" + new String(ch, start, length));
  }
  if (tag.equals("ADDR")) {
  System.out.println(" 地址:" + new String(ch, start, length));
  }
  }

  public void startElement(
  String uri,
  String localName,
  String qName,
  Attributes attrs) {
  tags.push(qName);
  }
  }

  10k消耗时间:110 47 109 78
  100k消耗时间:344 406 375 422
  1000k消耗时间:3234 3281 3688 3312
  10000k消耗时间:32578 34313 31797 31890 30328

  然后是 JDOM http://www.jdom.org/

  JDOM 的目的是成为 Java 特定文档模型,它简化与 XML 的交互并且比使用 DOM 实现更快。由于是第一个 Java 特定模型,JDOM 一直得到大力推广和促进。正在考虑通过“Java 规范请求 JSR-102”将它最终用作“Java 标准扩展”。从 2000 年初就已经开始了 JDOM 开发。

  JDOM 与 DOM 主要有两方面不同。首先,JDOM 仅使用具体类而不使用接口。这在某些方面简化了 API,但是也限制了灵活性。第二,API 大量使用了 Collections 类,简化了那些已经熟悉这些类的 Java 开发者的使用。

  JDOM 文档声明其目的是“使用 20%(或更少)的精力解决 80%(或更多)Java/XML 问题”(根据学习曲线假定为 20%)。JDOM 对于大多数 Java/XML 应用程序来说当然是有用的,并且大多数开发者发现 API 比 DOM 容易理解得多。JDOM 还包括对程序行为的相当广泛检查以防止用户做任何在 XML 中无意义的事。然而,它仍需要您充分理解 XML 以便做一些超出基本的工作(或者甚至理解某些情况下的错误)。这也许是比学习 DOM 或 JDOM 接口都更有意义的工作。

  JDOM 自身不包含解析器。它通常使用 SAX2 解析器来解析和验证输入 XML 文档(尽管它还可以将以前构造的 DOM 表示作为输入)。它包含一些转换器以将 JDOM 表示输出成 SAX2 事件流、DOM 模型或 XML 文本文档。JDOM 是在 Apache 许可证变体下发布的开放源码。

  Bean文件:

  package com.test;

  import java.io.*;
  import java.util.*;
  import org.jdom.*;
  import org.jdom.input.*;

  public class MyXMLReader {

  public static void main(String arge[]) {
  long lasting = System.currentTimeMillis();
  try {
   SAXBuilder builder = new SAXBuilder();
   Document doc = builder.build(new File("data_10k.xml"));
   Element foo = doc.getRootElement();
   List allChildren = foo.getChildren();
   for(int i=0;i<allChildren.size();i++) {
    System.out.print("车牌号码:" + ((Element)allChildren.get(i)).getChild("NO").getText());
    System.out.println(" 车主地址:" + ((Element)allChildren.get(i)).getChild("ADDR").getText());
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
  System.out.println("运行时间:" + (System.currentTimeMillis() - lasting) + " 毫秒");
  }
  }

  10k消耗时间:125 62 187 94
  100k消耗时间:704 625 640 766
  1000k消耗时间:27984 30750 27859 30656
  10000k消耗时间:OutOfMemoryError

  最后是 DOM4J http://dom4j.sourceforge.net/

  虽然 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,特别值得一提的是连 Sun 的 JAXM 也在用 DOM4J。

  Bean文件:

  package com.test;

  import java.io.*;
  import java.util.*;
  import org.dom4j.*;
  import org.dom4j.io.*;

  public class MyXMLReader {

  public static void main(String arge[]) {
  long lasting = System.currentTimeMillis();
  try {
   File f = new File("data_10k.xml");
   SAXReader reader = new SAXReader();
   Document doc = reader.read(f);
   Element root = doc.getRootElement();
   Element foo;
   for (Iterator i = root.elementIterator("VALUE"); i.hasNext();) {
    foo = (Element) i.next();
    System.out.print("车牌号码:" + foo.elementText("NO"));
    System.out.println(" 车主地址:" + foo.elementText("ADDR"));
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
  System.out.println("运行时间:" + (System.currentTimeMillis() - lasting) + " 毫秒");
  }
  }

  10k消耗时间:109 78 109 31
  100k消耗时间:297 359 172 312
  1000k消耗时间:2281 2359 2344 2469
  10000k消耗时间:20938 19922 20031 21078

  JDOM 和 DOM 在性能测试时表现不佳,在测试 10M 文档时内存溢出。在小文档情况下还值得考虑使用 DOM 和 JDOM。虽然 JDOM 的开发者已经说明他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处。另外,DOM 仍是一个非常好的选择。DOM 实现广泛应用于多种编程语言。它还是许多其它与 XML 相关的标准的基础,因为它正式获得 W3C 推荐(与基于非标准的 Java 模型相对),所以在某些类型的项目中可能也需要它(如在 JavaScript 中使用 DOM)。

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

  无疑,DOM4J是这场测试的获胜者,目前许多开源项目中大量采用 DOM4J,例如大名鼎鼎的 Hibernate 也用 DOM4J 来读取 XML 配置文件。如果不考虑可移植性,那就采用DOM4J吧!

你可能感兴趣的:(编程,xml,bean,应用服务器,领域模型)