以文本方式查看主题

-  中文XML论坛 - 专业的XML技术讨论区  (http://bbs.xml.org.cn/index.asp)
--  『 DTD/XML Schema 』  (http://bbs.xml.org.cn/list.asp?boardid=23)
----  技巧: 使用 StAX 部分解析 XML 文档  (http://bbs.xml.org.cn/dispbbs.asp?boardid=23&rootid=&id=12035)


--  作者: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 文档。


W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
4,406.250ms