以文本方式查看主题

-  中文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=12028)


--  作者:anchen0617
--  发布时间:11/13/2004 2:33:00 PM

--  技巧: 使用 StAX 高效筛选 XML 文档
使用 Streaming for XML (StAX),可以避免传统推式解析器的缺陷,从而有效地筛选 XML 文档。这篇技巧展示了如何从 XML 文档中检索特定信息,一旦收集到这些信息即停止解析过程。
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
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 文档。


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