![Java核心技术·卷Ⅱ:高级特性(原书第10版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/937/34339937/b_34339937.jpg)
3.5 使用命名空间
Java语言使用包来避免名字冲突。程序员可以为不同的类使用相同的名字,只要它们不在同一个包中即可。XML也有类似的命名空间(namespace)机制,可以用于元素名和属性名。
名字空间是由统一资源标识符(Uniform Resource Identifier,URI)来标识的,比如:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/159-2-i.jpg?sign=1739298464-WcksZGKJQwAcKApvDcRTqL5qMnTMHAO7-0-7f13092ad556af6cbdeac8d5fa4ceae8)
HTTP的URL格式是最常见的标识符。注意,URL只用作标识符字符串,而不是一个文件的定位符。例如,名字空间标识符:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/159-3-i.jpg?sign=1739298464-7rNcqIr4ZO6VYOBKz19X5x29l6E1hb3V-0-3b3a954ff25fae403f6e539ed2b33693)
表示了不同的命名空间,尽管Web服务器将为这两个URL提供同一个文档。
在命名空间的URL所表示的位置上不需要有任何文档,XML解析器不会尝试去该处查找任何东西。然而,为了给可能会遇到不熟悉的命名空间的程序员提供一些帮助,人们习惯于将解释该命名空间的文档放在URL位置上。例如,如果你把浏览器指向XML Schema的命名空间URL(http://www.w3.org/2001/XMLSchema),就会发现一个描述XML Schema标准的文档。
为什么要用HTTP URL作为命名空间的标识符?这是因为这样容易确保它们是独一无二的。如果使用实际的URL,那么主机部分的唯一性就将由域名系统来保证。然后,你的组织可以安排URL余下部分的唯一性,这和Java包名中的反向域名是一个原理。
尽管长名字空间的唯一性很好,但是你肯定不想处理超出必需范围的长标识符。在Java编程语言中,可以用import机制来指定很长的包名,然后就可以只使用较短的类名了。在XML中有类似的机制,比如:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/160-i.jpg?sign=1739298464-CZXnvISKYZYPTdnP9Q6H8TbaZTBCvLZ4-0-a3e97f1fe9d96966fc7f7653abae0261)
现在,该元素和它的子元素都是给定命名空间的一部分了。
子元素可以提供自己的命名空间,例如:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/160-2-i.jpg?sign=1739298464-tsih8PpQ7wJ8W1vlyOAOSi0zrpZrWL3L-0-78bff3db69151331b7336f92188ff35d)
这时,第一个子元素和孙元素都是第二个命名空间的一部分。
无论是只需要一个命名空间,还是命名空间本质上是嵌套的,这个简单机制都工作得很好。如若不然,就需要使用第二种机制,而Java中并没有类似的机制。你可以用一个前缀来表示命名空间,即为特定文档选取的一个短的标识符。下面是一个典型的例子:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/160-3-i.jpg?sign=1739298464-3ObU0PK5pbHRD0c5sWN2XWr6g6TOAVdH-0-42da34289da5536498356d949f7308f6)
下面的属性:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/160-4-i.jpg?sign=1739298464-PrDFrY4b2MfMRgK4oKQVo6HAW4GZur9e-0-479d565c651cd487888cfe50eeae3fe1)
用于定义命名空间和前缀。在我们的例子中,前缀是字符串xsd。这样,xsd:schema实际上指的是命名空间http://www.w3.org/2001/XMLSchema中的schema。
注意:只有子元素继承了它们父元素的命名空间,而不带显式前缀的属性并不是命名空间的一部分。请看下面这个特意构造出来的例子:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/160-5-i.jpg?sign=1739298464-PgXPM5bE8xkwzq67WDBcEYKjm4ocyZeX-0-827b5df1f5b227a8e5c48d9b91f01fa6)
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/161-i.jpg?sign=1739298464-caFGY22r6mej2prQJGdJCnwBRmaHXXFX-0-548c1a9255624426cb5f0a17ed152ea8)
在这个示例中,元素configuration和size是URI为http://www.horstmann.com/corejava的命名空间的一部分。属性si:unit是URI为http://www.bipm.fr/enus/3_SI/si.html的命名空间的一部分。然而,属性value不是任何命名空间的一部分。
你可以控制解析器对命名空间的处理。默认情况下,Java XML库的DOM解析器并非“命名空间感知的”。
要打开命名空间处理特性,请调用DocumentBuilderFactory类的setNamespace Aware方法:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/161-2-i.jpg?sign=1739298464-FftPARKSm6vuI2PFA5k4bblIknsoXjvs-0-4568f7074dcbea6b687baf39f3563f03)
这样,该工厂产生的所有生成器便都支持命名空间了。每个节点有三个属性:
·带有前缀的限定名(qualified),由getNodeName和getTagName等方法返回。
·命名空间URI,由getNamespaceURI方法返回。
·不带前缀和命名空间的本地名(local name),由getLocalName方法返回。
下面是一个例子。假设解析器看到了以下元素:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/161-3-i.jpg?sign=1739298464-Gp6NbYOALOied8RNB61cc9QQU0WGyNOR-0-c1e8179d29f4096690f5da718e588a08)
它会报告如下信息:
·限定名=xsd:schema
·命名空间URI=http://www.w3.org/2001/XMLSchema
·本地名=schema
注意:如果对命名空间的感知特性被关闭,getLocalName和getNamespaceURI方法将返回null。
org.w3c.dom.Node 1.4
·String getLocalName()
返回本地名(不带前缀),或者在解析器不感知命名空间时,返回null。
·String getNamespaceURI()
返回命名空间URI,或者在解析器不感知命名空间时,返回null。
javax.xml.parsers.DocumentBuilderFactory 1.4
·boolean isNamespaceAware()
·void setNamespaceAware(boolean value)
获取或设置工厂的namespaceAware属性。当设为true时,工厂产生的解析器是命名空间感知的。