经验表明,XML 名称空间是造成混乱的常见原因,也是困扰 XML 采用的主要因素之一。本文中,作者认为 XML 名称空间并没有为它要解决的问题提供一个很好的解决方案,在现实生活中,大多数的 XML 用例都不需要 XML 名称空间。作者建议放弃名称空间或者大大减少名称空间的使用。如果需要名称空间,那么开发人员应该使用最佳实践和惯例来限制规范所提供的语法自由度,使名称空间以更容易理解的一致面孔出现。
数年来我与大量的 XML 开发人员合作过,这些人中有偶尔接触 XML 的用户,也有技术专家。多数情况下,我发现他们缺乏对名称空间的理解,或者看起来理解,但在处理和解决和名称空间有关的问题时就会出现混乱。XML 名称空间,按照目前规范的定义,是从 perl 高手应该能在两周内创建一个 XML 解析器 这一信条得出的,但仅仅是理解 XML 名称空间的细枝末节,需要的时间就不止两周。XML 名称空间 FAQ 又增加了这种混乱(请参阅参考资料):
3.3) 除了元素类型和属性的两段式命名系统之外,XML 名称空间推荐标准还定义了其他东西吗? 没有。 这一点非常重要,也是很多混乱的来源,因此要重复一下: ...
如果接触过 XML 开发人员,那么可以问问有多少人认为自己确实理解了 XML 名称空间。我的经验是很少有人这样认为。我并不是第一个注意到这一点的人,XML 名称空间是各种 XML 开发人员邮件列表中经常受到尖刻批评的对象。公平地说,XML 名称空间规范是最受争议的 XML 基础规范之一。
|
既然存在关于 XML 名称空间的实际困难或者认识到的困难,那么使用它有什么好处呢?该规范的介绍提供了一些内幕:
我们预想到可能出现这样的可扩展标记语言(XML)应用,即一个 XML 文档可能包含多个软件模块定义的元素和属性(这里称之为“标记词汇表”),供多个软件模块使用。这样做的一个原因是模块化,如果存在这种很好理解的标记词汇表和相应的软件,那么更好的办法就是重用这些标记,而不是重新发明它们。 这种包含多种标记词汇表的文档带来了识别和冲突问题。即使遇到“冲突”,也就是说用于其他软件包但使用了相同元素类型名或属性名的标记,软件模块也必须能够识别那些自身设计应该处理的标签和属性。
因此,我们要解决的问题是识别和冲突,以及结合多种文档的用例。
现在来看一看 XML Namespace FAQ 中的一些论述:
3.1) XML 名称空间的用途是什么? XML 名称空间用来为元素和属性提供统一的惟一名称。它有多种用途,比如: - 组合来自不同文档的片段,而不会造成命名冲突。(参见下面的例子。) - 编写能够被特定的元素和属性调用的可重用代码模块。统一的惟一名称可以确保只有正确的元素和属性才能调用这些模块。 - 定义可以在其他模式或实例文档中重用的元素和属性,而不用担心命名冲突。比如,在部件目录中可以使用 XHTML 元素提供部件的描述。或者使用 XML 模式中定义的 nil 值来表示忽略的值。
下面的例子值得仔细研究。主要的问题在于,两个文档中的元素 Address
在不同上下文中表示不同的事物。到目前为止一切顺利。
于是问题就来了:
只要这些元素类型只出现在不同文档中就没有问题。但是如果在同一文档中组合使用这些元素类型,比方说在部门、部门地址及其 Web 服务器的列表中,会怎么样呢?
这里有一个问题声明。下面的例子消除了组合两个文档时对某一公共元素产生歧义。
文档组合起来是什么样子呢?FAQ 给出了一个例子:
清单 1. 用名称空间组合文档 <Department> <Name>DVS1</Name> <addr:Address xmlns:addr="http://www.tu-darmstadt.de/ito/addresses"> <addr:Street>Wilhelminenstr. 7</addr:Street> <addr:City>Darmstadt</addr:City> <addr:State>Hessen</addr:State> <addr:Country>Germany</addr:Country> <addr:PostalCode>D-64285</addr:PostalCode> </addr:Address> <serv:Server xmlns:serv="http://www.tu-darmstadt.de/ito/servers"> <serv:Name>OurWebServer</serv:Name> <serv:Address>123.45.67.8</serv:Address> </serv:Server> </Department> |
很好。现在看一看没有名称空间的相同文档,可以清楚地看到存在明显的冲突和识别问题:
清单 2. 没有名称空间的组合文档 <Department> <Name>DVS1</Name> <Address> <Street>Wilhelminenstr. 7</Street> <City>Darmstadt</City> <State>Hessen</State> <Country>Germany</Country> <PostalCode>D-64285</PostalCode> </Address> <Server> <Name>OurWebServer</Name> <Address>123.45.67.8</Address> </Server> </Department> |
哪一个更好呢?我认为您会认为第二个文档更清晰一些,Address
的不同用途似乎对软件编写没有困扰。
|
这就是令人奇怪的地方,XML Namespace 规范忽视了 XML 的基本支柱:XML 文档是层次性的,没有任何标签是孤岛(island)。
如果要告诉您我对哪一个 Address
元素感兴趣,我可以说“Server 地址,就是 Server
标签下的那个地址”或“Department 地址,Department
标签下的那个地址”。对于 XML 来说,它们分别是 /Department/Server/Address
和 /Department/Address
。没有不清楚的地方,您完全知道我说的是哪一个元素。这是因为 XML 标签是由上下文定义的,而不是由标签名定义的。
只有忽略上下文的时候才会出现含糊不清。但是为什么要忽略上下文呢?因此,您可以编写只能被 Address
元素触发而忽略其他标记的程序。如果对文档的结构不感兴趣,为何要费力使用 XML 呢?为什么要表达元素之间的父子关系呢?为什么不直接使用没有层次结构的名值对呢?
处理 XML 时忽略结构和层次根本就是错误的。新手在开发第一个基于 SAX 的程序时,可能会遇到识别和冲突的问题,但在开发第二个程序时,可能就会实现某种形式的状态保持机制。无论哪种情况,XML 名称空间都不能为开发人员提供什么帮助,它要比记录状态复杂得多。
|
如果在组合文档中消除标签的歧义对应用程序很重要,那么利用 XML 文档已有的上下文要比使用依赖于统一的惟一命名的新复杂模型简单得多。XML 名称空间所提出的冲突和识别解决方案,归根到底就是要为每个文档中的每个单独标签创建统一的惟一名称。
注意,这个例子的组合方式是人为的,不需要按这种方式组合文档。只需要设想组合文档是问题的根源即可。当然,在一个文档中也可以有多个名称相同但含义不同的元素。事实上,根据我的经验,这种情况比组合文档更常见。解决这一问题的办法就是为每个元素提供统一的惟一名称吗?如果要确保所有情况下每个元素都有惟一的名称,只需使用很长的、明确的名称即可。对此不需要名称空间。
解决方案是在层次上下文中处理元素。普通的 XML 已经提供了上下文 —— 不需要 XML 名称空间。
|
常见的 XML 应用,当然也包括 FAQ 提出的用例,不需要 XML 名称空间。但是,在某些情况下,名称空间或者类似的东西是有价值的。这一节分析一些很有说服力的例子。
第一,使用名称空间作为标识和控制文档类型版本的一种方法。可以使用如下所示结构:
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> |
这是 SOAP 1.2 消息最外层标签的一个例子。在这里,名称空间的用途很明确:通知消费者这一段 XML 是 SOAP 信封,它与万维网联盟(W3C)有关,并且符合该规范的 2003 年 5 月的版本。与没有使用名称空间的标签相比,这个名称空间确实提供了更多信息:
<Envelope> |
因此这是名称空间的一种用途!确实如此吗?这一证明当然很有用,但并不一定要用名称空间来获得。所有您所需要是一个属性和一个惯例,简而言之:通过最外层标签的 documentIdentifier
属性来标识文档:
<Envelope documentIdentifier="http://www.w3.org/2003/05/soap-envelope"> |
再看另一个例子。为类型信息提供惟一标识符,名称空间的作用很有说服力。您可能看到过这样的 XML 片段:
<element name="cost" type="xsd:float"/> |
或者
<element name="greeting" type="SOAP-ENC:string"/> |
其中的 xsd
和 SOAP-ENC
分别为指向 XML Schema 和 SOAP 编码类型的名称空间标识符。因此,cost
是 XSD 中定义的浮点类型的元素,greeting
是 SOAP 编码规范中定义的字符串类型的元素。下面是另一个类似的例子:
<cost xsi:type="xsd:float">29.95</cost> |
这表明 cost 有一个类型,所谓类型指的是 XSI 定义的类型,而这个类型又是 XSD 中定义的浮点数。关键在于,实际上我们正在寻找的是适用于每种类型的、惟一的、与上下文无关的标识符。不用将您的文档与 XSD 或 SOAP 编码文档组合,只需从文档中引用每种规范中的特定元素即可。这些规范甚至不一定是 XML,引用的是一个平面结构,仅仅是一个类型列表。如果确信类型结构是分层的,那么需要完全限定类型的路径,比如:
<cost xsi:type="xsd:/types/simple/float">29.95</cost> |
最后终于有了一个使用 XML 名称空间的好理由。但是,这段 XML 实际上要完成什么呢?沿着标签发送类型信息。这种情况并不常见,通常在单独的文件中用 DTD 或 XML Schema 定义类型信息,这样就不必在每次请求的每个标签中重复这些信息。
可能这仍然算不上是 XML 名称空间的一个合理的用例,但是您已经看到了一些用处。这一经验可以推广为:用外部引用指针关联元素的属性可能是有用的。元素本身并不需要名称空间,但是属性可能需要。
有人可能坚持认为,也可以使用其他比 XML 名称空间更简单的方法来实现这一点,不过我不准备再进行讨论了 —— 至少在本文中如此。相反,我认为这确实是一种非常特殊的情况,应该严格限制 XML 名称空间的使用,比如只在没有其他更好办法的情况下使用。
|
可能这些讨论使您认识到了 XML 名称空间的问题以及它有限的适用性。现在该怎么办呢?
我提倡积极地对抗 XML Namespace 规范,在一般的应用中排除它。再明确一点,我不是说完全不需要名称空间,只是说很少需要使用它,当前的 XML Namespace 规范造成了很多困扰。在一般 XML 文档中使用 XML 名称空间并不是最佳实践,事实上,这是一种代价高昂的实践。
无论如何,既然得到了广泛的采用,XML 名称空间不可能会自动消失。新的规范是否会显著改善这种情况也不是很明朗,最初制定该规范的那些人都非常聪明。
不过,更改一般的实践,从在每个新的 XML 和 Web 服务规范中都使用 XML 名称空间,转变为有节制地、仅在绝对必要的情况下使用它,从一般的规范中将名称空间排除掉,这样做是完全合理和非常谨慎的。
至少能够并且绝对必须做的一点是,为名称空间的应用模式开发最佳实践和惯例,使其更容易理解。目前,规范所提供的语法自由度允许在文档中的任何地方使用名称空间,表达同一个概念可以有数不清的办法。如果开发人员社区能够在某些方面更趋于一致,用一种语法表达一个概念,那么理解受名称空间困扰的文档就会简单得多。设想一下,如果看一眼受名称空间困扰的 XML 文档,就能够很容易地理解它,或者说,如果它确实是人类可读的,那该多好啊。不过,这将另一篇文章的主题。