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 提供了一个拉式解析器,可以让客户应用程序完全控制解析过程。客户应用程序可以决定何时中止解析过程,让解析器停下来也不需要什么诀窍。对于筛选而言这是非常理想的。
清单 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 文档。
- 您可以参阅本文在 developerWorks 全球站点上的
英文原文.
- 下载本技巧的
源文件。
- 在
Java Community Process 网站可以找到 Streaming API for XML(StAX)的更多信息。
- 阅读“
使用 XML 流解析器”,关于 StAX 的
developerWorks技巧系列的第一篇(2003 年 11 月)。
- StAX 技巧系列的第 2 篇介绍了如何对 StAX 解析器应用事件过滤器和流过滤器,“
使用 StAX 部分解析 XML 文档”(2003 年 12 月)。
- 在 Nicholas Chase 的
这篇技巧中,了解如何在文档中间停止 SAX 解析器而不丢失已经采集的数据。(
developerWorks,2002 年 6 月)。
- 在
developerWorks
XML 专区可找到更多的 XML 资源。最新的 XML 技巧完整列表,请查看
技巧汇总页面。
- 了解如何才能成为
IBM 认证的 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 联系。