在 Java 中使用 XML 是一个内容相当丰富的主题;可以使用多个 API而且许多 API 使得使用 XML 简单得如同从文本文档读取行。基于树的 API(如 DOM)展现了一个内存中的 XML 结构该结构对于 GUI 和编辑器來说是最理想的,基于流的 API(如 SAX)对于只需要获取文档数据的高性能应用程序来说很重要在本技巧文章系列中,我将从基础知识开始一步步地教您如何在 Java 中使用 XML同时,您将学习许多甚至连众多专业人士都不知道的诀窍所以即使您已经具有一些 XML 经验,也应该仔细阅读本敎程
会给予您一个良好的开端。特别在本篇技巧文章中我将讨论如何获取 SAX 解析器实例以及如何对该解析器设置一些基本功能和属性。
紸:我假设您已经下载了一个符合 SAX 的解析器(如 Apache Xerces-J)(请参阅 以获取链接)Apache 站点有大量关于如何进行设置的信息,但是基本上您只需要將已下载的 JAR 文件放入 CLASSPATH
中。这些示例假设您的解析器可用
使用 SAX 的第一步实际上是获取解析器实例。在 SAX 中由 org.xml.sax.XMLReader
类的实例表示解析器。我在上┅篇技巧文章(“Achieving vendor independence with SAX”— 请参阅 )中对它进行了详细讨论所以我将不会在这里花大量的时间在上面。清单 1 显示了在无需编写与供应商相关嘚代码的情况下获取新
SAX 解析器实例的正确方法
当然,您要确保指定的类存在并在类路径上
一旦有了解析器实例,就需要配置它请注意,这与设置解析器来处理 XML 中的错误、内容或结构不同;相反配置是实际告诉解析器如何操作的过程。您可以打开验证、关闭名称空间檢查及扩展实体这些行为完全独立于特定的 XML 文档,因此涉及与新解析器实例的交互
注:对于那些过于急燥的人来说(我知道您不是这樣的),我当然会处理内容、错误处理及类似的东西然而,这些主题将在未来的技巧文章中讨论所以您还得复查。眼下我们只关注配置、功能和属性。
可以用两种方法配置解析器:功能和属性 功能包括打开或关闭特定功能,比如验证 属性包括设置解析器所使用的特定项的值,如用来验证所有文档的模式位置我将先讨论功能,然后在下一节研究属性
功能是通过解析器上名为 setFeature()
的方法设置的,这一點并不奇怪语法类似于清单 2 所示。
这相当清楚不需要说明;关键是知道可用于 SAX 解析器的常见功能。每个功能均由一个特定的 URI 标识可鉯从 SAX 网站在线获得这些 URI 的完整列表(请参阅 )。一些最常见的功能是验证和名称空间处理清单 3 显示了设置这两种属性的示例。
请注意雖然解析器有几个标准 SAX 功能,但这些解析器可以自由地添加自己的特定于供应商的功能例如,Apache Xerces-J 添加了一些考虑动态验证以及遇到致命错誤之后继续处理的功能请参考解析器供应商的文档,以获取相关的功能 URI
一旦理解了功能,就很容易理解属性除了属性将对象作为参數而功能获取布尔值外,它们以完全相同的方式操作我们使用 setProperty()
方法来设置属性,如清单 4 所示
这里使用相同的错误处理框架,所以您可鉯容易地在两种类型的配置选项之间复制代码和功能一样,SAX 提供了一组标准属性供应商可以添加他们自己的扩展。常见的 SAX 标准的属性栲虑到了设置词法处理程序(Lexical Handler)和声明处理程序(Declaration Handler)(我将在以后的技巧文章中讨论这两个处理程序)像 Apache Xerces 之类的解析器对它们进行了扩展,例如使它们能够设置输入缓冲区大小以及要在验证中使用的外部模式的位置。清单 5 显示了几个实际使用中的属性
通过理解功能和屬性,您几乎可以让解析器做任何事情一旦掌握了用这种方式来设置解析器,您就可以准备阅读我的下一篇技巧文章该文章将讨论有關构建基本内容处理程序方面的知识。到那时我们将在 上再见。
在我的 中 您学习了如何设置 SAX 分析器的某些基本分析特性和属性(以 XMLReader
类實例的形式)。那些特性和属性全都跟分析器与之交互的所有 XML 文档的基本处理相关并且包括诸如验证、名字空间处理和实体扩充等内容。虽然这些当然是分析的重要方面但是它们不是为特定文档格式(比如处理在线商店订单的 XML 或者表示机械工厂库存的
XML)定制的。当谈到編写与分析过程本身交互的逻辑时您需要编写一个 SAX
这个接口中的每个方法都提供了一个将自定义代码插入 XML 分析过程的钩子(hook)。然而在罙入到每个方法的细节之前我将展示一个虚构的实现,并展示如何将该实现注册到分析器中清单 2 是一个简单的处理器 DummyHandler
,它为所有必需嘚 ContentHandler
方法提供空的方法体
现在,您可以创建一个 DummyHandler
实例并将它附加到分析器如清单 3 所示。
要确切理解分析器和 ContentHandler
之间的交互是如何进行的您需要首先理解什么是 回调方法。当 XML 分析器开始分析 XML 输入文档时它会遇到某些特殊的事件,比如文档的开头、元素中的字符数据以及元素的结尾这其中的每个事件都与 ContentHandler
接口中的某个特定方法具有某种关联;就这里的例子而言,相关的方法分别是 startDocument()
、 characters()
和 endElement()
对于每个事件,分析器都要
回调到内容处理器临时地把控制权连同关于该事件的某些信息(比如元素的名称或者正在处理的字符)转交给内容处理器。正是在這一点在回调方法中,您的编程逻辑参与进来了当回调方法结束时,程序流将返回到分析器分析过程将再次重复。
这听起来有点不恏理解因此我将提供一个例子。清单 4 中的例子类名为 HelloHandler
它具有非常简单的输出语句,这些语句嵌入到 ContentHandler
接口的每个方法中您可以使用它來进行一些简单的测试。
这足够简单了对吧?它展示了每个方法是在何时被调用的惟一还没有做的事情就是定义一个 XML 文档,然后让一切运行起来清单 5 显示了一个非常简单 XML 文档。
现在可以运行这个测试分析程序了——清单 6 中包括了一个完整的工作版本以供参考您应该會得到类似清单 7 所示的输出。
仔细观察一下这个输出您会对回调方法是如何使用的有个初步的认识:当分析开始时,文档定位器得到设置(我将在以后的一个技巧中详细讨论这点)文档的开头得到处理,然后处理流程继续往下进行首先是遇到一个元素的开头( some-element
),然後是一些内容之后是该元素的结尾,等等当然,这个处理器并不是很有用因为它通常很难确切告诉您当前正在处理哪个元素,尤其昰在有嵌套元素的情况下
但是由于时间和空间所限,我将把改进这个处理器的任务留给读者作为练习请试着编写这样一个 HelloHandler
版本(把它命名为 InfoHandler
),它会输出正在被调用的方法以及提供给该方法的参数这样将帮助
您更清楚地看到每次回调中所发生的事情。在下一个技巧中我将展示我自己为该处理器编写的代码,并将开始更深入地钻研回调看看每次回调究竟做了些什么事情。届时希望您有兴趣,我将茬新闻组中和您在线相见