级别: 中级 |
Neil Graham (neilg@ca.ibm.com) , XML 解析器开发经理, IBM
Elena Litani (elitani@ca.ibm.com) , 软件开发人员, IBM
2004 年 11 月
作 为一项成熟的技术,XML 空格(space)异常活跃。Java™ API for XML Processing (JAXP) 1.3 最近终于定案,它是很多与 XML 相关的最新开放标准进入 J2SE 平台的渠道。本文包括两部分,第一部分将描述 JAXP 1.3 API,作者 Neil Graham 和 Elena Litani 将简要介绍 JAXP 规范,并详细说明 javax.xml.parsers
包的变化,同时还将讨论强大的模式缓冲和验证框架。
JAXP 最初被命名为 Java API for XML Parsing,JAXP 1.0 仅仅为应用程序提供了创建 DOM Level 1 或者 SAX 1.0 解析器的厂商中立的方法。随着 JAXP 1.1 在 2001 年发布,“P”改为代表 ProcessingParsing,API 的功能得到进一步扩展,为应用程序提供了与 XSLT 处理程序进行交互的标准方法。JAXP 1.1 成为了 Java 2 Standard Edition (J2SE) 1.4 和 Java 2 Enterprise Edition(J2EE)1.3 的一部分。随着规范的小幅修订,2002 年发行了 JAXP 1.2,同时还增加了在 JAXP 兼容解析器中调用 W3C XML Schema 验证的一些标准方式。 而不是
JAXP 1.3 将成为 J2SE 5 和 J2EE 4 的一部分,在过去三年中,它是 JAXP API 最重要的一个版本。在以下两篇文章中,我们将探讨 JAXP 新版本中添加的各种新功能。
JAXP 1.3 概述
JAXP 规范支持下列规范并以这些规范为基础(请参阅参考资料):
- XML 1.0 (3rd Edition) 和 XML 1.1,W3C 推荐的规范。
- Namespaces in XML 1.0(包括 Errata)和 Namespaces 1.1,W3C 推荐的规范。
- XML Schema(包括 Errata),W3C 推荐的规范。
- XSL Transformations (XSLT) Version 1.0,W3C 推荐的规范。
- XML Path Language (XPath) Version 1.0(包括 Errata),W3C 推荐的规范。
- XML Inclusions (XInclude) Version 1.0,撰写本文时还是 W3C 提议的推荐的规范。
- Simple API for XML (SAX) 2.0.2 (sax2r3) 和 SAX Extensions 1.1。
所有兼容 JAXP 1.3 的实现都必须支持上述规范。
JAXP API 包括几个 Java 包,它们分别提供了 JAXP 的部分功能:
javax.xml
:这是一个根程序包。它只包括一个类(XMLConstants
),该类定义了一些有用的常量。javax.xml.parsers
:这个包从 JAXP 1.0 起就一直存在,它定义了厂商中立的使用 SAX 或 DOM 解析和验证 XML 文档的 API。javax.xml.transform
:这个包从 JAXP 1.1 开始出现,它定义了 XSL Transformation API。javax.xml.namespace
:这是 JAXP 1.3 中新增的一个包,它定义了用来操纵名称空间的QName
类和NamespaceContext
接口。这些类最初是在 Java API for XML-Based RPC (JAX-RPC) 规范(请参阅参考资料)中定义的。javax.xml.datatype
:这是 JAXP 1.3 中新增的一个包,它定义了新的 Java 类型,并完成了 W3C Schema 数据类型与 Java 类型之间的映射。javax.xml.validation
:也是 JAXP 1.3 中新增的一个包,它定义了用于应用程序缓冲模式(比如 W3C Schema)和验证 XML 文档的 API。javax.xml.xpath
:JAXP 1.3 中新增的一个包,它定义了一种数据模型和独立于实现的 API,以便在文档中应用 XPath 表达式。
JAXP 还包括 org.xml.sax
包(包含 SAX API)和 org.w3c.dom
包(包含 DOM Level 3 API,请参阅参考资料)。
JAXP 1.3 和 XML 解析
为 了确保依赖于特定 JAXP 版本的应用程序具有最大的可移植性,从一开始,JAXP 规范版本就与 DOM 和 SAX 的特定版本,以及底层的 XML 和 XML Namesapces 规范紧密联系在一起。自从上一个 JAXP 重要版本(JAXP 1.1)发布以来,过去三年中,这些规范没有一个是保持原封不动的,因此 JAXP 也升级到 1.3 版,以使这些规范能够进入 J2SE 和 J2EE。
XML 标准的演化
W3C 在 2004 年初最终确定了 XML 1.0 3rd Edition、XML 1.1 和 XML Namespaces 1.1。JAXP 1.3 要求所有符合规范的解析必须实现上述三个标准。虽然 XML 1.0 3rd Edition 包含的大多数都是一些澄清性的说明,只有对 XML 了解最细致的应用程序才会注意到这些说明,但是 XML 1.1 将对 XML 世界产生积极的影响,因为它极大地扩展了 XML 名称所能使用的字符。它允许 XML 向前兼容 Unicode Standard,同意 XML 和 Unicode 关于行尾标志的定义,并规定了除了 0 之外的所有 ASCII 字符的引用(包括所有控制字符)。XML Namespaces 1.1 允许名称空间前缀不在文档片段中声明,当然,它引用了 XML 1.1。进一步了解这些规范,请参阅 developerWorks 文章“XML 1.1 和 Namespaces 1.1 揭密”。
W3C 的另一个产品是 XML Inclusions (XInclude) 1.0,目前该标准是提议推荐的标准。XInclude 提供了 XML 文档可以全部或部分地包含其他 XML 文档和文本性资源的方法。与 XML 实体不同,它完全是在文档类型定义(DTD)之外完成的,因此,XInclude 对于 XML Schema 验证是友好的。有在多个文档间共享的内容的 XML 资源的作者将发现 XInclude 的非凡价值。JAXP 1.3 规定所有符合规范的实现都要跟踪该规范的发展,直至成为 W3C 推荐的规范。
至于 XML 解析 API 本身,JAXP 支持 SAX 2.0.2 和 SAX Extensions 1.1,以及 DOM Level 3 Core 和 DOM Level 3 Load and Save。DOM Level 3 规范本身是新增功能的重要部分,但不在本文的讨论之列。IBM developerWorks 已经有一些关于 DOM Level 3 Core 的很好的文章(请参阅参考资料)。有兴趣的读者可以去阅读这些文章。
如 同版本号的细微变化所预示的那样,与 JAXP 1.1 支持的 SAX 2.0 相比,SAX 2.0.2 没有显著的变化。SAX 2.0.1 包含一些签名兼容性的变化(因此没有得到 JAXP 1.2 的支持),比如,除此之外基本上与 SAX 2.0 相同之外,它为 SAX 定义的异常类添加了默认构造函数,为 EntityResolver#resolveEntity
回调函数的 throws 子句添加了 IOExceptions
。在新增特性中,SAX 2.0.2 定义了以下内容:
- 允许应用程序查询 SAX 解析器是否支持 XML 1.1 的特性。
- 指示解析器将 XML 名和名称空间保留在 JVM 中,如果要确定内部字符串是否相等,可以使用
==
String.equals()
。 而不是 - 支持 XML 1.1 规范化检查,注意,JAXP 1.3 不要求兼容解析器支持该特性。
对于原来的 SAX 扩展,Extensions 1.1 是一次重要的改进,它增加了以下内容:
EntityResolver2
接口是EntityResolver
的扩展,它为 DTD 外部子集提供了回调函数,并为resolveEntity
方法的参数列表添加了baseURI
和实体名。Attributes2
扩展了Attributes
,提供了诸如是否在 DTD 中声明属性、或者属性值是否为 DTD 中的默认值之类的信息。Locator2
扩展了Locator
,添加了getXMLVersion()
和getEncoding()
,为当前处理实体的 XML 声明上的伪属性提供了完全访问。
javax.xml.parsers 中的新增功能
JAXP 1.3 对其直接定义的解析相关接口的修改不大。可能最常用到的是 reset()
方法,DocumentBuilder
和 SAXParser
都添加了这个方法,从而使对象返回到默认状态。因为 JAXP 解析器对象的工厂机制非常昂贵,所以应用程序常常要实现 SAXParser
和 DocumentBuilder
的缓冲池,以便在遇到解析任务时能使用这些对象,而在解析完成后不一定要销毁它们。如果能够将这些对象重置为未知状态,使缓冲池不必了解请求使用对象的代 码使用对象的信息,也不要求使用解析器的代码知道所访问解析器以前的使用情况。这样可以使缓冲池效率更高,更易于实现。至于如何实现解析器缓冲池,请参阅 “Improve performance in your XML applications, Part 2”。
通过 SAXParserFactory
和 DocumentBuilderFactory
添加的 setSchema()
方法,可以将解析器连接到模式(请参阅下面关于 javax.xml.validation
包的讨论)。这样可以使解析器的构造函数针对特定的模式(javax.xml.validation.Schemas
)进行优化,相对于不具备内置验证文档语法知识的标准解析器对象,这样可以显著地改进性能。应用程序也可以配置其解析器工厂,让生成的解析器能够通过 get/setXIncludeAware
方法感知 XInclude,该方法是工厂新添加的。解析器和工厂都可以查询,确定是否通过 isXIncludeAware()
方法感知 XInclude,也可以通过 getSchema()
方法获得当前关联的 Schema
(如果有的话)。
验证和模式缓冲 JAXP API
很 多应用程序需要用模式验证 XML 文档,比如按照 W3C XML Schema 推荐标准定义的模式。为了验证文档,验证解析器需要先解析模式文档,然后在内存中构件内部的模式表示,最后使用内存中的模式验证 XML 文档。因此,如果在验证每个 XML 文档之前,都要构建模式的内存中表示,验证可能造成很大的性能代价。通常应用程序只使用有限的模式,因此希望处理器只构建一次给定模式的内存中的表示 (in-memory representation),然后使用它验证文档。
到目前为止,实现必须提供自己的模式缓冲机制。比如,Apache Xerces-J 解析器定义了自己的语法缓冲 API(请参阅参考资料)。现在 JAXP 1.3 定义了一种标准 API(javax.xml.validation
包),让应用程序重用模式,从而提高了整体性能。
现在来进一步考察验证 API。要检索模式的内存中表示,首先需要获得模式工厂的实例(javax.xml.validation.SchemaFactory
), 它规定了该工厂支持的特定模式语言。兼容的 JAXP 实现必须支持 W3C XML Schema,是否支持其他语言(如 RELAX NG)则是可选的。您可以按照类似配置 XML 解析器的方法,来使用特性和属性配置工厂,最后要求工厂为给定的模式构建内存中表示。模式的内存中表示被定义为 Schema
类,该类是恒定的,因此也是线程安全的。API 没有提供查询模式结构或属性的方法。
Schema
类的用法有两种:
- 可以构造针对给定 Schema 内存中表示优化的解析器验证(如前所述)。
- 通过
Schema
类创建验证程序,用Schema
验证不同的 XML 输入资源(如 DOM 或 SAX)。
首先,我们来说明如何重用给定模式的内存中表示来改善解析性能。为了简化起见,清单 1 的示例代码中使用了一个描述订单的 XML 文档(po.xml),以及一个订单模式(po.xsd)。文档和模式都是 W3C XML Schema Primer Recommendation 定义的(请参阅参考资料)。
首先构建一个模式工厂,并使用它建立订单模式的内存中表示。然后检索 DOM 工厂的实例,在该工厂上设置订单 Schema,使用这个 DOM 工厂创建 DOM 解析器。新建的解析器就可以用订单模式验证 XML 文档。
清单 1. 重用模式解析和验证 XML 文档现在来看一看如何使用验证器。从给定模式可以创建两种类型的验证器:
- 可以验证 DOM 或 SAX 源的
验证器
,分别产生 DOM 和 SAX 事件。 - 验证 SAX 时间流的
ValidatorHandler
。该验证器就像是 SAXContentHandler
。如果在验证器处理程序上设置自己的org.xml.sax.ContentHandler
,该验证器处理程序将充当过滤器,验证收到的 SAX 事件,并把事件转发到ContentHandler
。该验证器还允许您使用TypeInfoProvider
接口(请参阅ValidatorHandler.getTypeInfoProvider()
方法)检索元素和属性的类型信息。
这两种验证器都不是线程安全的。验证器可能改变结果数据,向原来的数据添加新的信息。比如,DOM 树中可能出现默认属性,或者作为验证结果出现新的 SAX 事件。您可以设置不同的特性和属性来配置验证器,注册实体解析器(org.w3c.dom.ls.LSResourceResolver
)帮助验证器解析外部实体,或者附加错误处理程序(org.xml.sax.ErrorHandler
)。注意,如果没有指定错误处理程序,则默认的实现对任何验证错误都抛出 SAXParseException
异常。
清单 2 说明如何使用 Validator
接口验证 DOM 文档。这里假设应用程序希望针对两种类型的模式验证 DOM 文档:po.xsd 和 ipo.xsd。应用程序可能已经从其他应用程序收到 DOM 文档,或者修改了原来的 DOM 文档,现在要确保根据 po.xsd 或 ipo.xsd 验证该 DOM 文档仍然是有效的。
Validator
接口验证 DOM 文档 结束语
本文提供了 JAXP API 的概述,其中包括基本 XML 标准的修订和解析 API 的一些变化。我们还详细讨论了新的 javax.xml.validation
包,介绍了应用程序如何利用它来改善性能。第 2 部分将介绍 JAXP 1.3 中提供的新数据类型、名称空间支持方面的一些通用工具、javax.xml.transform
包的一些变化、全新的 javax.xml.xpath
包及其数据模型和厂商中立的 XPath 1.0 API。
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文。
- 进一步了解 Java API for XML Processing (JAXP)。
- 在 W3C Technical Reports 中可以找到所有 W3C 规范。
- XML 文档 po.xml 和订单模式 po.xsd 都是在 W3C XML Schema Primer Recommendation 中定义的。
- 了解 Java API for XML-Based RPC (JAX-RPC)。
- 了解 Simple API for XML (SAX) 和 Document Object Model (DOM) 规范。
- 请参阅 Elena Litani 和 Arnaud Le Hors 合著的关于 DOM Level 3 的文章,该文章包括两部分:
- 第 1 部分介绍如何操纵和比较节点,以及如何处理文本和用户数据(developerWorks,2003 年 8 月)。
- 第 2 部分深入讨论自举、到 XML InfoSet 的映射、访问类型信息以及 Xerces 的用法(developerWorks,2003 年 8 月)。
- 了解 XML 1.1 和 Namespaces 1.1 是什么,它们带来了什么样的变化,对其他规范和用户的影响如何,请参阅 Arnaud Le Hors 撰写的 “XML 1.1 和 Namespaces 1.1 揭密” (developerWorks,2004 年 5 月)。
- 请参阅“改善 XML 应用程序性能”系列的所有文章: 第 1 部分(2004 年 7 月)、 第 2 部分第 3 部分(2004 年 9 月)。这些文章都发表在 developerWorks 上,您可以阅读这些文章,查看如何充分挖掘 XML 应用程序的性能。
(2004 年 7 月)和 - 了解 Xerces2 Java 解析器及其语法缓冲 API。
- 进一步考察 RELAX NG,它由 Organization for the Advancement of Structured Information Standards (OASIS) 维护,同时也是 OASIS 和 International Organization for Standardization (ISO) 标准。
- 如果被这些 XML 标准搞糊涂了,Uche Ogbuji 撰写的关于 XML 标准的 developerWorks 系列文章可以帮助您理清思路:
- 在 developerWorks XML 和 Java 技术专区中可以找到更多相关资源。
- 了解如何才能成为一名 IBM 认证的 XML 及相关技术的开发人员。