IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  XML  >

技巧: 使用 StAX 部分解析 XML 文档

对 StAX 解析器应用事件过滤器和流过滤器

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 中级

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 XMLModeling Business Objects with XML Schema(均由 Morgan Kauffman 出版),要了解这些书的更多信息,请访问 http://www.bdaum.de。可以通过 berthold.daum@bdaum.de 与 Berthold 联系。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款