级别: 初级 Berthold Daum (berthold.daum@bdaum.de), 总裁, BDaum Industrial Communications
2004 年 1 月 01 日 使用 Streaming for XML (StAX),可以避免传统推式解析器的缺陷,从而有效地筛选 XML 文档。这篇技巧展示了如何从 XML 文档中检索特定信息,一旦收集到这些信息即停止解析过程。
XML 文档筛选或者分类是一个常见的问题,特别是在 XML 中间件中。把 XML 文档交给特定的处理器可能需要同时分析文档类型和文档内容。问题在于以尽可能小的代价从文档中取得要求的信息。传统解析器如 DOM 或 SAX 不是非常适合这项工作。比如,DOM 需要解析全部文档并在内存中构造完整的文档树,然后才把控制交给客户。即使采用延后节点展开(因此可以部分解析文档)的 DOM 解析器,也有很高的资源要求,因为至少要在内存中部分构造文档树。出于文档筛选的目的而言,这是完全不能接受的。
和 DOM 类似,SAX 解析器也控制了整个解析过程。默然情况下,SAX 解析器从文档的开始处进行解析直到文件尾结束。在解析过程中,通过回调事件通知客户事件处理程序。在文档筛选中为了避免不必要的开销,这种事件处理程序可能希望在收集到必要的信息后停止解析过程。SAX 实现这种机制的常见技术是抛出异常,这种方法在 Nicholas Chase 的
developerWorks技巧文章“
Stop a SAX parser when you have enough data”中讨论过。这样将导致 SAX 终止解析过程。事件处理程序采集的信息必须编码在一个错误信息中,该错误信息包装在一个异常对象中提交给解析器客户。客户中一个专门的错误处理程序接收这种异常,并且必须在解析器错误消息中检索出需要的信息!这也可以作为筛选文档的一种方法,但这是一种复杂的方法。
进入 StAX
StAX 提供了一个拉式解析器,可以让客户应用程序完全控制解析过程。客户应用程序可以决定何时中止解析过程,让解析器停下来也不需要什么诀窍。对于筛选而言这是非常理想的。
清单 1 说明了一个简单的文档分类程序可能是什么样子。这个例子中我使用了基于指针的 StAX API。从文档的第一个起始标签开始(根元素标签),我从该元素中检索
kind 属性。然后把该属性的值回传给客户,解析过程也停止了。客户现在可以处理这个返回值了。
清单 1. 筛选文档
import java.io.*;
import javax.xml.stream.*;
public class Classifier {
// Holds factory instance
private XMLInputFactory xmlif;
public static void main(String[] args)
throws FileNotFoundException, XMLStreamException {
Classifier router = new Classifier();
String kind1 = router.getKind("somefile.xml");
String kind2 = router.getKind("otherfile.xml");
}
/**
* Return the document kind
* @param string - the value of the "kind" attribute of the root element
*/
private String getKind(String filename)
throws FileNotFoundException, XMLStreamException {
// Create input factory lazily
if (xmlif == null) {
// Use reference implementation
System.setProperty(
"javax.xml.stream.XMLInputFactory",
"com.bea.xml.stream.MXParserFactory");
xmlif = XMLInputFactory.newInstance();
}
// Create stream reader
XMLStreamReader xmlr =
xmlif.createXMLStreamReader(new FileReader(filename));
// Main event loop
while (xmlr.hasNext()) {
// Process single event
switch (xmlr.getEventType()) {
// Process start tags
case XMLStreamReader.START_ELEMENT :
// Check attributes for first start tag
for (int i = 0; i < xmlr.getAttributeCount(); i++) {
// Get attribute name
String localName = xmlr.getAttributeName(i);
if (localName.equals("kind")) {
// Return value
return xmlr.getAttributeValue(i);
}
}
return null;
}
// Move to next event
xmlr.next();
}
return null;
}
}
|
注意,我使用了一个实例字段保存
XMLInputFactory 实例。这样做是为了提高效率。和实际的解析过程(要快得多)相比,
XMLInputFactory.newInstance() 和
xmlif.createXMLStreamReader() 需要很大的开销。虽然每个文档都要执行一次
createXMLStreamReader() ,但您可以重用
XMLInputFactory 实例,从而避免反复执行
XMLInputFactory.newInstance() 。
下一步
这篇技巧说明了如何使用 StAX 解析器筛选和分类 XML 文档。在下一篇技巧中,我将介绍如何通过 StAX API 创建 XML 文档。
参考资料
关于作者  | |  | Berthold Daum 是德国 Ltzelbach 的一名顾问和作家。他的近作有
System Architecture with XML和
Modeling Business Objects with XML Schema(均由 Morgan Kauffman 出版),要了解这些书的更多信息,请访问
http://www.bdaum.de。可以通过
berthold.daum@bdaum.de与 Berthold 联系。
|
对本文的评价
|