 | 级别: 中级 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 来设计组件。那么,还有什么比
XMLReader 和
XMLFilter 更好的办法呢?
XMLReader 定义了一个接口,可以将 SAX 事件传递给应用程序。
XMLFilter 链接一系列阅读器,形成一个迷你管道。更重要的是,每个 XML 管道都使用同一个
XMLReader ,使得 XMLReader 可以成为一个通用的组件。
尽管
XMLReader 是最通用的解析器 API,许多人对它仍不太了解。这确实是不应该的。虽然 XML 解析器很复杂,而
XMLReader 却是十分简单的 API,它的方法属于以下种类中的一种:
- 大部分方法用来注册事件处理器,例如
setContentHandler() 或
setProperty() 。
- 某些方法用来控制事件流,例如
parse() 和
setFeature() 。
当然,这几类功能正是用于将 XML 文档输出为 SAX 事件的组件所需要的。正如您在下一节将要看到的,如果组件写一个 XML 文档,只需要不到一个小时的时间就可以将其包装为一个
XMLReader 。
实现 XMLReader
XMLReader 定义了一个十分简单的模型,用来控制 SAX 事件流。要说明组件如何实现 XMLReader,您可以在一个
XMLReader 中封装一个
ZipFile 。
ZipFile 是标准 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 组件开发人员应该考虑使用
XMLReader 。
XMLReader 为任何输出 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 及相关技术的开发人员。
关于作者
对本文的评价
|  |