管理 XML 数据: XML 编目

From "http://www.ibm.com/developerworks/cn/xml/x-mxd3.html"

管理 XML 数据: XML 编目
间接样式表、DTD 和模式
Elliotte Rusty Harold (elharo@metalab.unc.edu), 副教授, Polytechnic University


2005 年 6 月 30 日

一条老的程序员谚语说,通过增加间接层任何问题都能解决。这一谚语同样适用于 XML。加载模式、 DTD 和样式表出现的很多问题,都可以通过引入 XML 编目作为解析器和网络加载程序之间的间接层得到完美的解决。XML 编目允许文档消费者用一组 URL 替换 XML 文档自身中规定的实际 URL 或者公共标识符。这样可以提高 XML 处理的速度和安全性。
很多 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 文件请求。

系统标识符与 URI

同时拥有 uri 和 system 元素或者 rewriteURI 和 rewriteSystem 元素,似乎有点奇怪。实际上,所有的系统标识符都是 URI,从来没有一种结构同时包含 URI 和系统标识符。system 和 rewriteSystem 元素仅用于那些在 XML 1.0 规范中定义为系统标识符的事物,主要是文档类型声明和外部实体定义中使用的 URI。uri 和 rewriteURI 元素用于其他一切事物。


虽然我用单独的编目文件示范了每个元素,但是可以将所有这些都放在一个编目中。如果同一标识符有多个映射,则位置靠前的优先。如果一个资源有多个标识符(比如,同时有公共标识符和系统标识符的 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 解析器绕过防火墙,编目还可以改进安全性。因为您使用的很多工具可能已经内置了对编目的支持,所以编目很容易解决很多困难的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值