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

developerWorks 中国  >  XML  >

技巧: 实现 XMLReader

XML 转换器的一个接口

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 中级

Benoit Marchal (bmarchal@pineapplesoft.com), 顾问, Pineapplesoft

2003 年 3 月 01 日

在这篇技巧中,Benoit Marchal 介绍了用于 XML 管道的 API。他认为大家熟悉的 XMLReader 接口适用于很多 XML 组件。

最流行的用于处理 XML 文档的设计是 管道。管道包含一组组件,每个组件负责 XML 文档处理过程中的一个步骤。文档从一个组件流向下一个组件,逐步形成最后的输出。管道将复杂的过程分成很多较简单的步骤,因而促进了模块化的设计。管道同时也具备灵活性:增加或删除一个组件就可以改变处理过程。

管道 API

javax.xml.transform API 实现了一个简单的管道,其中包含三个组件:

  • 一个阅读器( Source 类)。
  • 一个转换器( Transformer 类)。
  • 一个串行化器( Result 类)。

有些项目,比如 Cocoon、Jelly 以及 GNU 实现了更加复杂的管道。但是,不利于组件开发人员的是,每个项目都有不同的 API,也就是说很难开发一个可以集成到所有三个项目中的组件。

为了减小端口工作量,一个可选的做法是围绕基本和通用的 API 来设计组件。那么,还有什么比 XMLReaderXMLFilter 更好的办法呢? XMLReader 定义了一个接口,可以将 SAX 事件传递给应用程序。 XMLFilter 链接一系列阅读器,形成一个迷你管道。更重要的是,每个 XML 管道都使用同一个 XMLReader ,使得 XMLReader 可以成为一个通用的组件。

尽管 XMLReader 是最通用的解析器 API,许多人对它仍不太了解。这确实是不应该的。虽然 XML 解析器很复杂,而 XMLReader 却是十分简单的 API,它的方法属于以下种类中的一种:

  • 大部分方法用来注册事件处理器,例如 setContentHandler()setProperty()
  • 某些方法用来控制事件流,例如 parse()setFeature()

当然,这几类功能正是用于将 XML 文档输出为 SAX 事件的组件所需要的。正如您在下一节将要看到的,如果组件写一个 XML 文档,只需要不到一个小时的时间就可以将其包装为一个 XMLReader





回页首


实现 XMLReader

XMLReader 定义了一个十分简单的模型,用来控制 SAX 事件流。要说明组件如何实现 XMLReader,您可以在一个 XMLReader 中封装一个 ZipFileZipFile 是标准 Java API 的一部分(在 java.util.zip 包中),可以简化包含 ZIP 文件的目录的读取。

SAX 事件

可以写 XML 文档的组件都包含输出标签和文本内容的方法。文本输出方法会转义一些特殊字符(如 < ) 。要使用这些输出方法,您可以编写以下代码:

writeStartTag("zip:Entry");
writeContent(name);
writeEndTag("zip:Entry");

要想将组件转变成一个 XMLReader ,第一步是将这三个方法分别替换成它们的 SAX 等价方法: startElement()characters()endElement() 。这些 SAX 方法名称虽然很长,但是使用起来并不难:

contentHandler.startElement(NS_URI,"Entry","zip:Entry",attributes);
contentHandler.characters(name.toCharArray(),0,name.length());
contentHandler.endElement(NS_URI,"Entry","zip:Entry");

下一步就是把这些代码放入 parse() 方法中。清单 1 是从 ZIP 阅读器中摘录的一段相关代码(要查看完整清单,请参阅 参考资料)。该代码从 ZIP 目录产生一个 XML 文档(作为 SAX 事件)。确保您正确地声明了 XML 名称空间。只调用 startPrefixMapping() 是不够的,因为应用程序可以通过将 http://xml.org/sax/features/namespace-prefixes 特性设置为 true 来请求 xmlns: 属性。


清单 1. parse() 方法摘录
if(source.getSystemId() != null && contentHandler != null)
{
   URL url = new URL(source.getSystemId());
   ZipFile file = new ZipFile(url.getPath());
   Enumeration enum = file.entries();
   contentHandler.startDocument();
   contentHandler.startPrefixMapping("zip",NS_URI);
   if(namespacePrefixes)
      attributes.addAttribute("","zip","xmlns:zip","CDATA",NS_URI);
   contentHandler.startElement(NS_URI,"File","zip:File",attributes);
   attributes.clear();
   while(enum.hasMoreElements())
   {
      ZipEntry entry = (ZipEntry)enum.nextElement();
      String name = entry.getName();
      contentHandler.startElement(NS_URI,"Entry","zip:Entry",attributes);
      contentHandler.characters(name.toCharArray(),0,name.length());
      contentHandler.endElement(NS_URI,"Entry","zip:Entry");
   }
   contentHandler.endElement(NS_URI,"File","zip:File");
   contentHandler.endPrefixMapping("zip");
   contentHandler.endDocument();
}
else
   throw new FileNotFoundException("InputSource has no system id");

Getter 和 setter

下一步就是为 SAX 使用的各种接口实现 getter 和 setter 方法。这是原始的 Java 代码。清单 2 是内容处理器的一个例子。


清单 2. getter 和 setter 方法
public ContentHandler getContentHandler()
{
   return contentHandler;
}
public void setContentHandler(ContentHandler value)
   throws NullPointerException
{
   if(value == null)
      throw new NullPointerException("ContentHandler");
   else
      contentHandler = value;
}





回页首


使用阅读器

XML 管道可以很好地处理 XML 文档。由于管道没有标准的 API,组件开发人员只能使用其他一些基本 API(比如 XMLReader )来进行开发。就像这篇技巧中所介绍的, XMLReader 不只是在解析器中使用,它可以用于任何写 XML 文档的工具。

为了进一步说明,清单 3 展示了 ZIP 阅读器如何与 Java 转换器交互。注意,这段代码初始化一个 copy转换器(没有样式表),并通过这个方法将 XML 文档存入文件。


清单 3. 使用阅读器
XMLReader reader = XMLReaderFactory.createXMLReader("org.ananas.tips.ZipReader");
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
Source source = new SAXSource(reader,new InputSource(args[0]));
Result result = new StreamResult(System.out);
transformer.transform(source,result);

正如这篇技巧所展示的,XML 组件开发人员应该考虑使用 XMLReaderXMLReader 为任何输出 XML 的组件提供了一个标准 API。 XMLReader 并不是一个实现起来十分复杂的 API,因为它包含的大部分是 SAX 事件处理器的 setter 和 getter 方法。最后要介绍的是,每个管道都可以将一个 XMLReader 作为起始点,这使得 XMLReader 成为一个可移植的 API。



参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.

  • 请参与 Benoit Marchal “使用 XML” 专栏的 讨论论坛



  • 下载这篇文章中使用的 源代码



  • 请阅读 Brett McLaughlin 所写的 “ 设置 SAX 解析器”一文( developerWorks,2003 年 7 月),了解 SAX 解析器的初始化。



  • 请阅读 Benoit Marchal 的文章“ 完成 XI”( developerWorks,2002 年 7 月),了解一个更复杂的在 SAX API 中打包 XML 生成器的例子。



  • 请阅读 Benoit Marchal 的文章“ SAX,功能强大的 API”( developerWorks,2001 年 8 月),了解更多有关 SAX 的信息。



  • 请阅读 Jelly,这是来自 Apache Jakarta 项目的一种 XML 语言,该项目提供了 XML 管道。



  • 请参阅 Apache 的 Cocoon项目(也来自 Apache),这是围绕 XML 管道建立的 XML 服务器。



  • 请参阅 GNU JAXP,这是 JAXP 的一个开放源代码实现,包含很多扩展,特别是包含管道 API。



  • developerWorks XML专区上查找更多 XML 资源。关于最新 XML 技巧的全部列表,请访问 实用技巧



  • 了解如何成为一名 IBM 认证的 XML 及相关技术的开发人员




关于作者

Author photo

Benoit Marchal 是一位比利时籍顾问。他是 XML by Example, Second Edition 和其他 XML 书籍的作者。可以向 Benoit 寻求有关 XML 项目的帮助。可以通过 bmarchal@pineapplesoft.com或者他的个人站点 marchal.com与他联系。




对本文的评价










回页首


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