您的位置:首页 > 编程语言 > Java开发

Java xml解析器SAX解析与StAX解析比较

2016-05-21 09:17 645 查看
最近了解到JDK6添加一个名为 StAX 的新解析方法,具体可以参考 在JDK 6.0 中基于 StAX 分析 XML 数据 。在J2ME 下可以使用 XmlPullParser ,参考 http://www.javaeye.com/topic/41564
。 这些解析方法都是pull parser 。“pull parser 为什么快? sax parser 为什么慢?我觉得这是关键。有人曾经在 delphi 上把一个 sax parser 改装成 pull parser 。其实很简单,只要把回调的 API 改成基于循环的主动查询。做的是减法 ” !!看完了下面的例子 http://www.javaeye.com/topic/763949
,似乎明白了一点。这个例子所使用的xml 文件和 Java 实体类和上面两个例子一样的。

Pull解析和 Sax 解析很相似,都是轻量级的解析。Pull 解析和 Sax 解析不一样的地方有

(1)pull 读取 xml 文件后触发相应的事件调用方法返回的是数字

(2)pull 可以在程序中控制想 解析到哪里就可以停止解析。[1]

SAX解析

  SAX是一个用于处理XML的事件驱动的“推”模型。它不是W3C标准,但它是一个得到了广泛认可的API,大多数SAX解析器在实现的时候都遵循标 准。SAX解析器不象DOM那样建立一个整个文档的树型表示,而是在读取文档时激活一系列的事件。这些事件被推给事件处理器,而事件处理器则提供对文档内 容的访问。事件处理器有三种基本类型:

  用于访问XML DTD内容的DTDHandler;

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

  用于访问文档内容的最普遍类型ContentHandler。


  解析器读取输入文档并在处理文档时将每个事件推给MyContentHandler。

下面的例子:打印出一本书的名称信息。

  首先,编写一个ContentHandler实现类,该类建立在DefaultHandler类基础上,并替换你感兴趣的事件类别所用的方法。该代码 抛弃来自DefaultHandler类的其他事件。定制的ContentHandler类提供回调方法,必须处理状态管理,操作开始元素事件、结束元素 事件和字符事件--为所有元素而不仅仅是title元素。

public class MyContentHandler extends DefaultHandler {boolean isTitle;

  public void startElement(String uri, String localName, String qName, Attributes atts) {

  if (localName.equals(title))

  isTitle = true;

  }

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

  if(localName.equals(title))

  isTitle = false;

  }

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

  if(isTitle)

  System.out.println(new String(chars, start, length));

  }

}

其次,为SAX解析器配置你的定制ContentHandler,然后该解析器开始处理XML文档。该解析器产生相应的一些事件并在从头至尾读取文档时将这些事件推给ContentHandler。

  SAXParser saxParser = new SAXParser();

  MyContentHandler myHandler = new MyContentHandler();

  saxParser.setContentHandler(myHandler);

  saxParser.parse(new File(books.xml));

与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 有一个内置的过滤机制,可以很轻松地输出一个文档子集或进行简单的文档转换。最后,SAX解析对根据DTD和XML Schema进行确认非常有用。实际上,Oracle在内部使用SAX解析器来完成这种确认,比起DOM来使用更少的内存并获得更高的效率。

StAX解析

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

  尽管SAX向ContentHandler返回不同类型的事件,但StAX却将它的事件返回给应用程序,甚至可以以对象的形式提供事件。

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

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

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

  下面的示例使用指针模型从上述的XML书籍目录中打印出书名信息。

XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(

  new FileInputStream(books.xml));

  while(reader.hasNext()) {

  int eventType = reader.next();

  if (eventType == XMLEvent.START_ELEMENT && reader.getLocalName().equals(title)) {

  reader.next();

  System.out.println(reader.getText());

  }

}

在这个示例中,reader建立后,应用程序通过reader.next()方法调用请求下一个事件。这使得StAX解析器将指针移到下一个事件的位 置。如果这个事件指明了一个名为“title”的元素的开始,则该应用程序代码将再一次调用reader.next(),向前移动指针,然后通过 reader.getText()方法调用获得title元素的文本。

  下面的示例使用迭代程序模型,在这种方法下事件以对象的形式被返回。

  XMLEventReader eventReader = XMLInputFactory.newInstance().createXMLEventReader(

  new FileInputStream(books.xml));

  while(eventReader.hasNext()) {

  XMLEvent event = eventReader.next();

  if (event instanceof StartElement &&((StartElement)event).getLocalName().equals(title))

  {

  System.out.println( ((Characters)eventReader.next()).getData());

  }

  }

  在这个示例中,应用程序请求下一个事件,使StAX解析器前进到下一个事件位置,并返回相应的事件对象。应用程序可以使用返回书名信息的getData()方法通过事件对象访问内容。 下一步

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

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

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

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

实际应用

两个方法,做一样的工作。

/**

* SAX parses the XML document in the provided zedFile param and creates a

* matching {@link ManagedElement}

*

* @param zed

* @return

*/

public ManagedElement parseManagedElement(String zed)

{

initManagedElement();

SAXParserFactory parserFactory = SAXParserFactory.newInstance();

try

{

SAXParser parser = parserFactory.newSAXParser();

ZedHandler handler = new ZedHandler();

parser.reset();

parser.parse(new ByteArrayInputStream(zed.getBytes("UTF-8")), handler);

postProcessReferences();

optionallyAddZedAsConfiguration(zed);

}

catch (ParserConfigurationException e)

{

throw new RuntimeException(e.getMessage());

}

catch (SAXException e)

{

if (e.getMessage().contains("XML document structures must start and end within the same entity"))

{

throw new RuntimeException(e.getMessage() + "\nMalformed ZED:\n" + zed);

}

else

{

throw new RuntimeException(e.getMessage());

}

}

catch (IOException e)

{

throw new RuntimeException(e.getMessage());

}

return managedElement;

}

/**

* StAX parses the XML document

* @param zed

* @return ManagedElement

*/

public ManagedElement stAXparseManagedElement(String zed)

{

initManagedElement();

XMLInputFactory inputFactory = XMLInputFactory.newInstance();

try {

InputStream input = new ByteArrayInputStream(zed.getBytes());

XMLStreamReader reader = inputFactory.createXMLStreamReader(input);

while (reader.hasNext()) {

reader.next();

}

postProcessReferences();

optionallyAddZedAsConfiguration(zed);

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (XMLStreamException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

return managedElement;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: