-- 作者:anchen0617
-- 发布时间:11/13/2004 2:56:00 PM
-- 技巧: 使用 StAX 部分解析 XML 文档
解析 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 文档。
|