IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  XML  >

技巧: 使用 XML 流解析器

使用 Streaming API for XML 更有效地解析 XML

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 中级

Berthold Daum (berthold.daum@bdaum.de)), 总裁, BDaum Industrial Communications

2003 年 12 月 01 日

这篇技巧展示了如何利用与规范一起部署的 StAX 参考实现,有效地使用 Streaming API for XML (StAX) 解析 XML 文档。Berthold Daum 解释了 StAX 中可用的两个 API 层:迭代器风格的 API 和基于指针的 API。

XML 拉式解析器以其卓越的性能和低内存占用而闻名。但是由于功能有限,直到最近 XML 拉式解析器还仅限于特定的应用。StAX 改变了这一切,它是 Java Community Process JSR-173 (请参阅 参考资料)的成果。随着规范进程进入最后阶段,各个厂商很快将推出用于实际应用的版本实现。

使用 StAX

对于多数应用程序员而言,与 Simple API for XML (SAX) 相比, StAX 将更加直观更容易使用。有两个 API 层 可供程序员选择:

  • 方便的、容易使用的、迭代器风格的 API。
  • 快速的、基于底层指针的 API。

无论使用哪一种,客户应用程序都可以完全控制解析过程和维护事件循环。这是拉式解析器和推式解析器的主要区别,如 SAX 自行组织事件循环并通过回调通知客户应用程序。





回页首


使用迭代器风格的 API

我们从迭代器风格的 API 开始。 XMLEventReader 实例通过 next() 方法提交 XMLEvent 类型的对象。通过调用适当的 API,您可以确定事件的类型和细节。或者可以使用 Java 语言的 instanceof 操作符确定具体的事件子类型。


清单 1. 迭代 XML 事件
import java.io.*;
import java.util.Iterator;
import javax.xml.namespace.QName;
import javax.xml.stream.*;
import javax.xml.stream.events.*;
public class ParseByEvent {
public static void main(String[] args)
throws FileNotFoundException, XMLStreamException {
// Use the reference implementation for the XML input factory
System.setProperty("javax.xml.stream.XMLInputFactory",
"com.bea.xml.stream.MXParserFactory");
// Create the XML input factory
XMLInputFactory factory = XMLInputFactory.newInstance();
// Create the XML event reader
FileReader reader = new FileReader("somefile.xml");
XMLEventReader r =
factory.createXMLEventReader(reader);
// Loop over XML input stream and process events
while(r.hasNext()) {
XMLEvent e = r.next();
processEvent(e);
}
}
/**
* Process a single XML event
* @param e - the event to be processed
*/
private static void processEvent(XMLEvent e) {
if (e.isStartElement()) {
QName qname = ((StartElement) e).getName();
String namespaceURI = qname.getNamespaceURI();
String localName = qname.getLocalPart();
Iterator iter = ((StartElement) e).getAttributes();
while (iter.hasNext()) {
Attribute attr = (Attribute) iter.next();
QName attributeName = attr.getName();
String attributeValue = attr.getValue();
}
}
if (e.isEndElement()) {
QName qname = ((EndElement) e).getName();
}
if (e.isCharacters()) {
String text = ((Characters) e).getData();
}
if (e.isStartDocument()) {
String version = ((StartDocument) e).getVersion();
String encoding = ((StartDocument) e).getCharacterEncodingScheme();
boolean isStandAlone = ((StartDocument) e).isStandalone();
}
}
}

XML Information Set 的其他对象类型,如注解、处理指令或者实体,也可以类似地检测和处理。





回页首


使用基于指针的 API

尽管迭代器风格的 API 非常方便和易于使用,它也带来了一些开销。解析器需要创建事件对象,这些对象在以后被无用单元收集器回收。对于高性能极其重要的应用程序,您可以选择基于指针的 API。 XMLStreamReader 类型的特点是 next() 方法提交一个整数值(而不是事件对象)表示事件类型。客户机应用程序可以查询阅读器获得其他的信息。


清单 2. 基于指针的 XML 处理
import java.io.*;
import javax.xml.stream.*;
public class ParseByIterator {
public static void main(String[] args)
throws FileNotFoundException, XMLStreamException {
// Use reference implementation
System.setProperty(
"javax.xml.stream.XMLInputFactory",
"com.bea.xml.stream.MXParserFactory");
// Create an input factory
XMLInputFactory xmlif = XMLInputFactory.newInstance();
// Create an XML stream reader
XMLStreamReader xmlr =
xmlif.createXMLStreamReader(new FileReader("somefile.xml"));
// Loop over XML input stream and process events
while (xmlr.hasNext()) {
processEvent(xmlr);
xmlr.next();
}
}
/**
* Process a single event
* @param xmlr - the XML stream reader
*/
private static void processEvent(XMLStreamReader xmlr) {
switch (xmlr.getEventType()) {
case XMLStreamConstants.START_ELEMENT :
processName(xmlr);
processAttributes(xmlr);
break;
case XMLStreamConstants.END_ELEMENT :
processName(xmlr);
break;
case XMLStreamConstants.SPACE :
case XMLStreamConstants.CHARACTERS :
int start = xmlr.getTextStart();
int length = xmlr.getTextLength();
String text =
new String(xmlr.getTextCharacters(), start, length);
break;
case XMLStreamConstants.COMMENT :
case XMLStreamConstants.PROCESSING_INSTRUCTION :
if (xmlr.hasText()) {
String piOrComment = xmlr.getText();
}
break;
}
}
private static void processName(XMLStreamReader xmlr) {
if (xmlr.hasName()) {
String prefix = xmlr.getPrefix();
String uri = xmlr.getNamespaceURI();
String localName = xmlr.getLocalName();
}
}
private static void processAttributes(XMLStreamReader xmlr) {
for (int i = 0; i < xmlr.getAttributeCount(); i++)
processAttribute(xmlr, i);
}
private static void processAttribute(XMLStreamReader xmlr, int index) {
String prefix = xmlr.getAttributePrefix(index);
String namespace = xmlr.getAttributeNamespace(index);
String localName = xmlr.getAttributeName(index);
String value = xmlr.getAttributeValue(index);
}
}

类似地,您也可以检测和处理其他事件类型,如 CDATAENTITY_REFERENCESTART_DOCUMENT 。根据我使用参考实现的体会, XMLStreamReader 解析文档比 XMLEventReader 快大约 30%。





回页首


下一步

这篇技巧演示了使用 Streaming API for XML (StAX) 的两个不同层次解析 XML 文档。在以后的技巧中,我将介绍 StAX 在具体场景中的应用以及如何通过 StAX 创建 XML 文档。



参考资料



关于作者

Berthold Daum 是德国 Ltzelbach 的一名顾问和作家。他的近作有 System Architecture with XMLModeling Business Objects with XML Schema(均由 Morgan Kauffman 出版),要了解这些书的更多信息,请访问 http://www.bdaum.de。可以通过 berthold.daum@bdaum.de与 Berthold 联系。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款