很多 XML 文档包含定位样式表、模式和 DTD 等的相对 URL。如果是绝对 URL,它们也可能指向隐藏在防火墙背后的系统。即使这些 URL 是可访问的,出于性能的考虑也可能需要使用本地缓存,而不是反复地绕过半个地球从同一个远程网络服务器上下载相同的 DTD。
比如 IBM developerWorks 站点使用的 XML 模板,其开头是这样的:
<?xml version="1.0"?> <?xml-stylesheet type="application/xml+xslt" href=" C:\IBM developerWorks\article-author-package\developerworks\xsl\dw-document-html-4.0.xsl" ?> <dw-document xsi:noNamespaceSchemaLocation= "C:\IBM developerWorks\article-author-package\developerworks\schema\dw-document-4.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
注意其中对 C:\IBM developerWorks\article-author-package\developerworks\xsl 目录中的样式表和 C:\IBM developerWorks\article-author-package\developerworks\schema 目录中的模式的引用。这些是 Microsoft® Windows® 操作系统中的路径名。我在 Mac 机器上撰写文章,将这些文件保存在不同的位置。因此,撰写文章之前首先要修改这些 URL 指向我的文件系统:
<?xml-stylesheet type=" application/xml+xslt " href="../developerWorks/xsl/dw-document-html-4.0.xsl" ?> <dw-document xsi:noNamespaceSchemaLocation= "../developerWorks/schema/dw-document-4.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
完成文章的第一稿之后,我将其发送给编辑。因为她的机器运行 Windows,处理这篇文章之前,她必须修改这些 URL,使其指向新的样式表和模式位置。她编辑完后将稿件返回来让我处理她提出的质疑,我又将所有的 URL 改回来。然后我将修改后的稿件返回给她,她将文章转发给 developerWorks 的产品组,产品组还要将这些 URL 修改到第三个地址。这个过程不仅仅是一般的效率低。
通过维护一个标准 URL 和系统标识符列表并将其映射到特定位置的副本,XML 编目可以解决这个问题。每个用户都可以将通用文件(如模式、DTD 和样式表)保存在不同的地方,只要他修改本地编目文件和保存的位置匹配即可。当解析器、样式表处理程序、模式验证器或其他工具读取文档时,它就可以从编目而非文档本身中的 URL 加载辅助文件。
除了简化作者和编辑的工作外,编目还有几方面的优点。比方说,假设您正从一个远程网站(比如 www.w3.org)读取 XHTML 文档。这类文档通常包含这样的 DTD:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
如果解析器读取 DTD,不仅必须从远程 Web 服务器加载 XML 文档,还必须从有可能更远的 http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd 读取 DTD。网络速度和延迟成为关键。使用编目可以要求解析器加载同一 DTD 的本地副本,速度要快得多。
URL 重定向还可以抵抗某些攻击。比如,向您的系统提供 XML 文档的某个人可以改变外部 DTD 子集的系统标识符,从而改变验证的 DTD。编目让解析文档的用户选择使用的 DTD,而不是编辑文档的人来选择。但是这种重定向不能提供完全的保护,因为少数攻击可能使用内部 DTD 子集作为向量,而编目并不影响内部 DTD。
除了简单的缓存功能外,编目还可以替换 DTD 或者模式。比如,您可能希望使用 XHTML DTD 的某种变体,它只定义了实体而没有声明任何元素或属性。即使从本地系统加载完整的 DTD,这种 DTD 解析和应用起来也要快得多。通过修改特定属性的 ATTLIST 声明还可以改变默认属性值。无论基于什么原因选择编目,结果都是一样的:编目让阅读文档的人而非编辑文档的人负责 DTD(或者模式、样式表)。
清单 1 显示了一个简单的编目。编目本身是一个 XML 文档。根元素是 urn:oasis:names:tc:entity:xmlns:xml:catalog 名称空间中的 catalog。这个编目包含三个 public 元素,每一个都从特定的公共标志符映射到特定的 URL。比如,公共标志符 ID -//W3C//DTD XHTML 1.0 Strict//EN 被映射到了 URL file:///opt/xml/xhtml/DTD/xhtml1-strict.dtd。
清单 1. 用于 XHTML 的简单编目
<?xml version='1.0'?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<public publicId="-//W3C//DTD XHTML 1.0 Transitional//EN"
uri="file:///opt/xml/xhtml/DTD/xhtml1-transitional.dtd "/>
<public publicId="-//W3C//DTD XHTML 1.0 Strict//EN"
uri="file:///opt/xml/xhtml/DTD/xhtml1-strict.dtd "/>
<public publicId="-//W3C//DTD XHTML 1.0 Frameset//EN"
uri="file:///opt/xml/xhtml/DTD/xhtml1-frameset.dtd "/>
</catalog> |
假设用该编目配置的解析器读取开头如下所示的文档:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
就不需要进行第二次网络连接从 http://www.w3.org 下载 DTD。相反,是从路径 /opt/xml/xhtml/DTD/xhtml1-strict.dtd 处的本地文件系统进行下载。
当然,编目也可以重定向到 http URL 或者相对 URL。比如,可以引用本地网络服务器而非远程服务器上的 DTD 副本,或者引用与源文档位于同一目录的 DTD。
编目可允许使用带有 systemId 属性的 system 元素来重新映射系统标识符,而不是使用带有 publicId 属性的 public 元素。这种重映射可能对仅供系统标识符引用而公共标志符不引用的 DTD 和实体定义很有用,清单 2 显示了如何使用重映射来根据 W3C 站点 URL 而非公共标志符加载 XHTML DTD 的本地副本。(清单 2 实际上只是为了举例说明,公共标志符通常更为可靠。)
清单 2. XHTML 基于系统标识符的编目
<?xml version='1.0'?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<system systemId="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
uri="file:///opt/xml/xhtml/DTD/xhtml1-transitional.dtd "/>
<system systemId="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
uri="file:///opt/xml/xhtml/DTD/xhtml1-strict.dtd "/>
<system systemId="http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
uri="file:///opt/xml/xhtml/DTD/xhtml1-frameset.dtd "/>
</catalog> |
对于一般不通过系统或公共标志符引用的样式表和其他文件,可以使用 uri 元素。该元素的 name 属性指定了映射自 的 URI。uri 属性规定了映射到 的 URI。清单 3 说明了如何将对 http://schemas.xmlsoap.org/wsdl/soap/ 的请求重定向到 http://localhost:8888/schemas/soap.xsd。
清单 3. 从本地 Web 服务器上加载 SOAP 模式
<?xml version='1.0'?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<uri name="http://schemas.xmlsoap.org/wsdl/soap/"
uri="http://localhost:8888/schemas/soap.xsd "/>
</catalog> |
编目对于改写整个 URL 树很有用。rewriteSystem 和 rewriteURI 元素为来自特定服务器或目录中的所有文件指定了替代位置。 清单 4 说明了如何将对 http://www.example.com/data/ 目录中文件的请求重定向到 http://www.example.net/mirror/。
清单 4. 改写 URI 的代码
<?xml version='1.0'?> <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog"> <rewriteURI uriStartString=" http://www.example.com/data/" rewritePrefix=" http://www.example.net/mirror/ "/> </catalog> |
比方说,如果解析器是用该编目请求文件 http://www.example.com/data/tic/article.xsl,那么实际上得到的文件是 http://www.example.net/mirror/tic/article.xsl。改写以前缀为基础,而且仅限于前缀。因此不能,比方说,使用 rewriteURI 将所有 html 文件请求重定向到 .xhtml 文件请求。
虽然我用单独的编目文件示范了每个元素,但是可以将所有这些都放在一个编目中。如果同一标识符有多个映射,则位置靠前的优先。如果一个资源有多个标识符(比如,同时有公共标识符和系统标识符的 DTD),在行为取决于系统,虽然可以在编目元素中使用 prefer="system" 或 prefer="public" 属性表明应该选择哪一个。
编目还有几个更高级的特性可用于更复杂的重定向,其中包括:
- 用于相对 URL 解析的
xml:base属性 - 用于为特殊类型的公共和系统标识符加载附加编目的
delegatePublic和delegateSystem元素 - 用于将多个编目串在一起的
nextCatalog元素 - 用于组合多个实体
group元素 - 由文档序言中的
<?oasis-xml-catalog?>处理指令指定的文档专用编目
不过,public、system、rewriteSystem、uri 和 rewriteURI 足以应付最常见的情况。
大量 XML 软件已经内置了对 XML 编目的支持。比如,Gnome Project 中的 libxml C 库自动加载 /etc/xml/catalog 中的编目。通过在 $XML_CATALOG_FILES 环境变量中指定新的位置可以改变查找编目的目录。如果不愿意加载任何编目,可以将 $XML_CATALOG_FILES 设为空字符串。
如果程序是用 Java™ 语言编写的,并使用 SAX 解析器读取 XML,那么可以安装 Norm Walsh 的编目筛选程序(现在属于 Apache XML Commons Project)作为 EntityResolver。类似地,TrAX URIResolver 可用于解析 XSLT 样式表 xsl:import 和 xsl:include 元素以及 document() 函数中的 URL。比如,下面的代码配置 SAX 解析器使用编目:
EntityResolver resolver = new org.apache.xml.resolver.tools.CatalogResolver(); XMLReader reader = XMLReaderFactory.createXMLReader(); reader.setEntityResolver(resolver); |
CatalogResolver 对象参阅 xml.catalog.files Java 系统属性以寻找编目。该属性包含分号分隔的编目文件 URI 列表。
Apache Forrest 文档框架和 Apache Cocoon Web 发布框架都使用这个 XML Commons CatalogResolver 类和编目文件来挑选出所服务的文档中的链接。
针对其他主要工具、库和环境也存在类似的选项。如何加载编目文件请参阅相关文档。虽然激活编目支持的细节因不同的工具和库而异,但编目格式都是一致的。
世界永远不会统一到单一的文件布局结构。XML 文档在系统间的移动破坏了到样式表、模式、DTD 和其他元内容的链接。XML 编目提供了一个有用的间接层次,即使文件不在文档所期望的位置也能够保持链接的完整性。只要希望保持 XML 文档及其辅助文件在异构系统中的同步,而不是简单的镜像拷贝,编目就能起到非凡的作用。通过加载本地缓存的副本而不是远程网络资源,编目还可以提高 XML 处理的速度。最后,通过避免交换 DTD 和防止 XML 解析器绕过防火墙,编目还可以改进安全性。因为您使用的很多工具可能已经内置了对编目的支持,所以编目很容易解决很多困难的问题。
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文。
- 阅读 OASIS XML Catalog 规范。
- 可以从 Apache XML Commons Project 下载 Norm Walsh 的 Catalog Resolver。
- 阅读 Elliotte Harold 所著
Effective XML
中的 Item 47,进一步了解 Catalog Common Resources。
-
Gnome Project’s libxml 默认启用编目解析。
- 下载 Apache XML Commons 编目解析程序,它捆绑在 Apache Forrest 文档框架 和 Apache Cocoon Web 发布框架 中。
- 进一步了解 DB2,IBM 的信息管理软件解决方案。其核心是一个强大的关系数据库管理系统(EDBMS)服务器家族。
- 在 developerWorks XML 专区可以找到更多 XML 资源。
- 了解如何才能成为一名 IBM 认证的 XML 及相关技术的开发人员。
Elliotte Harold 出生在新奥尔良,现在他还定期回老家喝一碗美味的秋葵汤。不过目前,他与妻子 Beth 定居在纽约临近布鲁克林的 Prospect Heights,与他们住在一起的还有猫咪 Charm(取自夸克)和 Marjorie(取自他岳母的名字)。他是 Polytechnic 大学计算机科学系的副教授,讲授 Java 技术和面向对象编程。他的 Cafe au Lait 网站是 Internet 上最受欢迎的独立 Java 站点之一,姊妹站点 Cafe con Leche 是最受欢迎的 XML 站点之一。他的著作包括 Effective XML 、 Processing XML with Java 、 Java Network Programming 和 The XML 1.1 Bible 。他目前正在研究处理 XML 的 XOM API 和 Jaxen GUI 查询工具。