|  | 级别: 中级 Berthold Daum (berthold.daum@bdaum.de), 总裁, BDaum Industrial Communications
2003 年 12 月 01 日 上一篇技巧中介绍的 Streaming API for XML(StAX), 不仅提供了一个快捷、易用、占用内存少的 XML 解析器,它还提供了过滤器接口,允许程序员向应用程序业务逻辑隐藏不需要的文档细节,这篇技巧介绍如何将事件过滤器和流过滤器用于 StAX 解析器。和第一篇技巧一样,我将同时使用迭代器风格的 API 和基于指针的 API 进行说明和介绍。
解析 XML 文档时,
XMLEventReader 实例通过它的
next() 方法向客户传递事件对象,文档中的每个语法单位都有一个事件。但是,应用程序不一定愿意接受所有的事件类;只查看 XML 元素及其属性的应用程序并不关心代表注释和处理指令的事件。幸运的是,StAX 允许您通过实现事件过滤器忽略某些事件类。
清单 1 给出了一个忽略所有 XML 处理指令的事件过滤器。这些事件没有传递给事件阅读器的
hasNext() 、
next() 或
peek() 方法。向给定的事件阅读器增加过滤器,必须构造一个新的阅读器,可以通过工厂方法
createFilteredReader() 完成。该方法接收原来的阅读器和
EventFilter 作为参数。接下来我使用这个新的事件过滤阅读器解析文档。
清单 1. 过滤 XML 事件
import java.io.*;
import javax.xml.stream.*;
import javax.xml.stream.events.XMLEvent;
public class ParseFilteredByEvent {
public static void main(String[] args)
throws FileNotFoundException, XMLStreamException {
// Use reference implementation
System.setProperty(
"javax.xml.stream.XMLInputFactory",
"com.bea.xml.stream.MXParserFactory");
// Create the XML input factory
XMLInputFactory factory = XMLInputFactory.newInstance();
// Create event reader
FileReader reader = new FileReader("somefile.xml");
XMLEventReader eventReader = factory.createXMLEventReader(reader);
// Create a filtered reader
XMLEventReader filteredEventReader =
factory.createFilteredReader(eventReader, new EventFilter() {
public boolean accept(XMLEvent event) {
// Exclude PIs
return (!event.isProcessingInstruction());
}
});
// Main event loop
while (filteredEventReader.hasNext()) {
XMLEvent e = filteredEventReader.next();
System.out.println(e);
}
}
}
|
用同样的方式,你可以从主应用程序逻辑隐藏其他事件类。您甚至可以通过分层的方式组合几个
EventFilter ,在另一个事件过滤阅读器的基础上构建新的过滤阅读器。
隐藏文档分支
在下面的例子中,我要介绍的过滤器能够忽略 XML 文档的整个分支。这一次我使用基于指针的 API 和流过滤阅读器而不是事件阅读器,因为我发现复杂的过滤器最好用流过滤器实现。与上面的例子类似,新的流过滤阅读器建立在基本流阅读器的基础上:
清单 2. 创建流过滤阅读器
// Create stream reader
XMLStreamReader xmlr =
xmlif.createXMLStreamReader(new FileReader("somefile.xml"));
// Create a filtered stream reader
XMLStreamReader xmlfr = xmlif.createFilteredReader(xmlr, filter);
|
其中第二个参数所用的
StreamFilter 由
清单 3 给出。在 XML 元素开始和结束的时候,它把相应的元素名和一个路径片段进行比较。路径作为一个
QName 数组实现,指定应该忽略文档的哪些部分。在这个例子中,路径 invoice/item 中的所有元素将被忽略。
实现这种过滤器必须要知道,每次激活
hasNext() 、
next() 或
peek() 方法时都会调用过滤器的
accept() 方法。因此,同一个事件可能多次调用
accept() 方法。在这里,我保证对每个事件过滤逻辑只执行一次,只有当文档中的字符位置发生变化时才会执行。
清单 3. 流过滤器
// Exclusion path
private static QName[] exclude = new QName[] {
new QName("invoice"), new QName("item")};
private static StreamFilter filter = new StreamFilter() {
// Element level
int depth = -1;
// Last matching path segment
int match = -1;
// Filter result
boolean process = true;
// Character position in document
int currentPos = -1;
public boolean accept(XMLStreamReader reader) {
// Get character position
Location loc = reader.getLocation();
int pos = loc.getCharacterOffset();
// Inhibit double execution
if (pos != currentPos) {
currentPos = pos;
switch (reader.getEventType()) {
case XMLStreamConstants.START_ELEMENT :
// Increment element depth
if (++depth < exclude.length && match == depth - 1) {
// Compare path segment with current element
if (reader.getName().equals(exclude[depth]))
// Equal - set segment pointer
match = depth;
}
// Process all elements not in path
process = match < exclude.length - 1;
break;
// End of XML element
case XMLStreamConstants.END_ELEMENT :
// Process all elements not in path
process = match < exclude.length - 1;
// Decrement element depth
if (--depth < match)
// Update segment pointer
match = depth;
break;
}
}
return process;
}
};
|

 |

|
下一步
这篇技巧示范了在 StAX 解析器中使用过滤器。在下一篇技巧中,我将介绍如何利用这些技术和其他技术来有效地筛选(screen)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 联系。
|
对本文的评价
|  | IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。 |