Java Web开发详解
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第3章 XML名称空间

本章要点

• 掌握名称空间声明的两种形式

• 掌握名称空间在元素中的运用

• 掌握名称空间在属性中的运用

• 理解名称空间的应用会导致文档有效性出现问题

一个XML文档中可以包含许多元素与属性,当我们使用其他人的XML文档,或者在文档中使用多个DTD文件时,就有可能碰到名称相同的元素,而这些名称相同的元素可能代表了完全不同的含义。比如:title可以用于表示标题,也可以用于表示某个人的头衔;table可以用于表示表格,也可以用于表示桌子。当这些具有相同名称不同含义的元素混合到一个文档中时,就会造成理解和处理上的混乱。为了解决这个问题,就要用到W3C发布的另一个推荐标准——XML名称空间,读者可以在http://www.w3.org/TR/REC-xml-names/上查看该规范的详细内容。

那么什么是名称空间呢?比方说,在北京,有两个地方叫做小营,我们坐出租车的时候,说去小营,有时候司机就会走错地方,所以,我们在说去小营的时候,总是说“去亚运村的小营”或者“去清河的小营”,当我们加上一个名称空间的时候(亚运村或清河),就构成了一个完整的限定名,这样司机就不会走错了。XML名称空间的概念与此类似,也是通过给元素或属性加上一个名称空间,来唯一标识一个元素或属性。实际上名称空间的概念在很多程序设计语言中早已存在,在Java中也有类似的概念,我们可以把Java中的包看成是它里面所有类和接口的名称空间,类是类中所有变量和方法的名称空间。

3.1 声明名称空间

名称空间通过使用一系列的保留属性来声明,这种属性的名字必须是以“xmlns”或以“xmlns:”作为前缀。与其他任何XML属性一样,这些属性可以直接或以默认的方式给出。

有以下两种形式的名称空间声明。

(1)第一种形式:<元素名xmlns:prefixname="URI">

元素名是指你在哪一个元素上声明名称空间,在这个元素上声明的名称空间适用于声明它的元素和属性,以及该元素内容中的所有元素及其属性。xmlns:prefixname作为该元素的属性名,属性的值是一个URI引用,是标识该名称空间的名称空间名字。其中prefixname给出名称空间前缀的名字,该前缀用于将元素及属性的名字与URI关联在一起。需要注意的是,在这样的声明中,名称空间的名字不能为空("")。如果有两个URI,其组成字符完全相同,就可以认为它们标识了同一个名称空间。

来自于XML名称空间的名字可以作为限定名(qualified names)出现,限定名包含了一个以冒号(:)分隔的名称空间前缀和一个本地部分(local part)。映射到URI引用的名称空间前缀选择了一个名称空间。

我们看一个例子:

        <hr xmlns:hr="http://www.sunxin.org/hr">

这个例子声明了一个名称空间,它的名字就是http://www.sunxin.org/hr,因为URI可以包含一些在XML名称中不允许的字符,而且太长,所以需要有一个简短的名称空间前缀,在本例中,就是hr,它和名称空间名字(http://www.sunxin.org/hr)相关联。这样,就可以直接使用这个前缀,作为元素或属性限定名的一部分,从而标识这个元素或属性属于哪一个名称空间。例如:<hr:employee>,hr是限定名的名称空间前缀部分,employee是限定名的本地部分,表示这个元素属于hr所关联的名称空间。当XML处理器解析文档时,将前缀替换成所关联的URI。

提示

名称空间推荐标准中指出,在名称空间声明中,使用相对URI引用已经被废弃了(不赞成使用)。

名称空间前缀可以是不包含冒号的任何合法的XML名称。

在声明名称空间时,有两个前缀是不允许使用的,它们是xml和xmlns。xml前缀只能用于XML 1.0规范中定义的xml:space和xml:lang属性,前缀xml被定义为与名称空间名字http://www.w3.org/XML/1998/namespace绑定。前缀xmlns仅仅用于声明名称空间的绑定,它被定义为与名称空间名字http://www.w3.org/2000/xmlns/绑定。

(2)第二种形式:<元素名xmlns="URI">

这种声明形式没有给出名称空间的前缀名,URI所标识的是默认的名称空间。在这样的默认声明中,属性值可以为空("")。我们看一个例子:

        <hr xmlns="http://www.sunxin.org/hr">

表示声明了一个默认的名称空间,hr元素及其内容中所有的没有前缀的元素都属于http://www. sunxin.org/hr所标识的名称空间,除非被hr元素内容中其他的默认名称空间声明所覆盖。

注意

在声明名称空间时,选择的URI不需要指向实际的内容,在URI所标识的位置上,可以不存在任何东西。在名称空间声明中的URI,只是形式上的标识符,其唯一的目的是提供一个唯一的名字。

3.2 名称空间在元素和属性中的运用

下面我们通过几个例子来看一下名称空间在元素和属性中的运用。

3.2.1 名称空间在元素中的运用

例3-1是一个关于图书的格式良好的XML文档,但是它有一个问题,title元素既用于表示书名,又用于表示作者的头衔,两个同名的元素却具有不同的含义。为了解决这个问题,我们决定采用名称空间,如例3-2所示。

例3-1

        <?xml version="1.0" encoding="GB2312"?>
        <books>
            <book>
              <title>JSP深入编程</title>
              <author>
                  <name>张三</name>
                  <title>作家</title>
              </author>
            </book>
            <book>
                <title>XML从入门到精通</title>
                <author>
                    <name>李四</name>
                    <title>教师</title>
                </author>
            </book>
        </books>

例3-2

        <?xml version="1.0" encoding="GB2312"?>
        <books xmlns:people="http://www.sunxin.org/people">
            <book>
                <title>JSP深入编程</title>
                <author>
                    <people:name>张三</people:name>
                    <people:title>作家</people:title>
                </author>
            </book>
            <book>
                <title>XML从入门到精通</title>
                <author>
                    <people:name>李四</people:name>
                    <people:title>教师</people:title>
                </author>
            </book>
        </books>

我们声明了一个名字为http://www.sunxin.org/people的名称空间,将前缀people与名称空间名字相关联,然后给元素author下的子元素name和title都附加了前缀。现在我们很容易就能区分书名和作者的头衔,前者以title元素表示,后者以people:title元素表示。

注意

在例3-2中,名称空间http://www.sunxin.org/people的声明范围适用于<books>元素和它的属性,以及该元素内容中的所有元素及其属性,但是只有具有名称空间前缀people的元素和属性才存在于声明的名称空间中。也就是说,元素books并不存在于名称空间中。

一个元素也可以有多个名称空间前缀的声明作为属性,如例3-3所示。

例3-3

        <?xml version="1.0" encoding="GB2312"?>
        <books:books xmlns:people="http://www.sunxin.org/people"
                xmlns:books="http://www.sunxin.org/books">
            <books:book>
                <books:title>JSP深入编程</books:title>
                <books:author>
                    <people:name>张三</people:name>
                    <people:title>作家</people:title>
                </books:author>
            </books:book>
            <books:book>
                <books:title>XML从入门到精通</books:title>
                <books:author>
                    <people:name>李四</people:name>
                    <people:title>教师</people:title>
                </books:author>
            </books:book>
        </books:books>

要注意的是,在名称空间的声明中,重要的是URI而不是前缀。前缀可以改变,只要URI不变,文档的含义就不变。在下面的例子(例3-4)中使用前缀p和bks代替people和books,但是该文档和例3-3的效果是一样的。

例3-4

        <?xml version="1.0" encoding="GB2312"?>
        <bks:books xmlns:p="http://www.sunxin.org/p"
            xmlns:bks="http://www.sunxin.org/bks">
            <bks:book>
              <bks:title>JSP深入编程</bks:title>
              <bks:author>
                  <p:name>张三</p:name>
                  <p:title>作家</p:title>
              </bks:author>
            </bks:book>
            <bks:book>
              <bks:title>XML从入门到精通</bks:title>
              <bks:author>
                  <p:name>李四</p:name>
                  <p:title>教师</p:title>
              </bks:author>
            </bks:book>
        </bks:books>

提示

名称空间的声明不一定要放在根元素上,可以根据需要在任何元素上声明名称空间。

3.2.2 默认名称空间

在具有很多元素(这些元素都在同一个名称空间中)的文档中,给每个元素名称都添加一个前缀将是一件繁琐的事情。为此,我们可以使用没有前缀名的xmlns属性将默认的名称空间附加给元素及其子元素,元素本身及其子元素都被认为是在默认的名称空间中,除非它们有明确的前缀。下面我们看一个例子,如例3-5所示。

例3-5

        <?xml version="1.0" encoding="GB2312"?>
        <books xmlns:p="http://www.sunxin.org/p"
            xmlns="http://www.sunxin.org/bks">
          <book>
              <title>JSP深入编程</title>
              <author>
                  <p:name>张三</p:name>
                  <p:title>作家</p:title>
              </author>
          </book>
          <book>
              <title>XML从入门到精通</title>
              <author>
                  <p:name>李四</p:name>
                  <p:title>教师</p:title>
              </author>
            </book>
        </books>

books元素本身(没有前缀)和它的内容中所有没有前缀的元素(book、title、author)都属于默认名称空间http://www.sunxin.org/bks,而author元素的子元素name和title则属于http://www.sunxin. org/p名称空间。要注意的是,如果元素books有前缀,那么就只有它的内容中所包含的没有前缀的元素属于默认名称空间。

默认名称空间声明中的URI可以设为空字符串,这样的话,在它的声明范围内,没有前缀的元素将被认为不存在于任何的名称空间中,这和没有声明默认名称空间是一样的。我们看一个例子,如例3-6所示。

例3-6

        <?xml version="1.0" encoding="GB2312"?>
        <books xmlns="http://www.sunxin.org/bks">
            <book>
                <title>JSP深入编程</title>
                <auhtor xmlns="">
                    <name>张三</name>
                    <title>作家</title>
                </auhtor>
            </book>
        </books>

author元素本身和它内部的name和title子元素不存在于默认名称空间http://www.sunxin.org/bks中,也不存在于任何其他的名称空间中。

3.2.3 名称空间在属性中的运用

因为属性是属于某个元素的,我们很容易就能把它与其他元素具有相同名称的属性区分开来,所以给属性添加名称空间不如给元素添加名称空间那么重要。但如果有必要,仍然可以给属性添加名称空间。

在例3-7中,auhtor元素的属性id_card存在于http://www.sunxin.org/people名称空间中。

例3-7

        <?xml version="1.0" encoding="GB2312"?>
        <books xmlns:bks="http://www.sunxin.org/bks"
                xmlns:p="http://www.sunxin.org/people">
            <bks:book>
                <bks:title>JSP深入编程</bks:title>
                <bks:author p:id_card="CH-100-200">
                    <p:name>张三</p:name>
                    <p:title>作家</p:title>
                </bks:author>
            </bks:book>
        </books>

要注意的是,一个属性要想在某个名称空间中,必须给该属性显式地加上名称空间的前缀,没有前缀的属性不在任何的名称空间中(包括默认的名称空间)。即使拥有属性的元素在某个名称空间中,没有前缀的属性仍然不在该名称空间或任何其他的名称空间中。

在XML文档中,没有任何一个标签可以包含两个相同的属性。所谓相同有两种情况:一种是属性的名字完全相同;另一种是属性限定名中的本地部分完全相同,而不同的前缀绑定到了相同的名称空间名字。下面我们通过两个例子(例3-8和例3-9)来说明名称空间在属性中运用时要注意的问题。

例3-8

        <?xml version="1.0" encoding="GB2312"?>
        <x xmlns:n1="http://www.w3.org"
          xmlns:n2="http://www.w3.org" >
          <!--错误,两个属性的名字相同-->
          <bad a="1"     a="2" />
          <!--错误,前缀n1和n2绑定的是同一个名称空间名字,而本地部分也完全相同-->
          <bad n1:a="1"  n2:a="2" />  ①
        </x>

① 曾经有读者问我,“对于①处的XML代码在使用IE浏览时,为什么没有报错呢?而你的书上说前缀n1和n2绑定的是同一个名称空间名字,而本地部分也完全相同,IE应该会报错啊”。

实际上,即使你使用XMLSpy浏览①处的代码,XMLSpy也不会报错。这是因为这两个软件内置的XML解析器默认没有打开对名称空间的支持。如果我们在解析XML文档时,打开了对名称空间的支持,那么①处的代码将会引发错误。

例3-9

        <?xml version="1.0" encoding="GB2312"?>
        <x xmlns:n1="http://www.w3.org"
          xmlns="http://www.w3.org" >
          <!--正确,属性名不相同-->
          <good a="1"     b="2" />
          <!--正确,没有前缀的属性a不在任何的名称空间中,即使是默认的名称空间也不例外-->
          <good a="1"     n1:a="2" />
        </x>

3.3 名称空间和DTD

在名称空间这一节的例子中,我们没有用到DTD,这是因为当XML文档应用了名称空间后,其有效性就有可能出现问题。

这是因为DTD和名称空间并不相关,当XML处理器在验证文档的有效性时,根本不管元素前缀的含义,只是按照DTD的规范对文档进行有效性验证。我们在声明名称空间时,使用了xmlns或xmlns:prefixname属性,而在验证时,会发现在DTD中没有声明该属性,当然验证就会失败。另外,如果在DTD中声明的是book元素,而在文档中使用的是bks:book元素,则文档也是无效的。所以,为了让使用名称空间的文档有效,我们必须在DTD中像声明其他属性一样声明xmlns或xmlns: prefixname属性,此外,还需要重写所有带有前缀的元素和属性的声明。例如,以前有个文档没有使用名称空间,如例3-10所示。

例3-10

        <?xml version="1.0" encoding="GB2312"?>
        <!DOCTYPE book [
        <!ELEMENT book (title, author)>
        <!ELEMENT title (#PCDATA)>
        <!ELEMENT author (#PCDATA)>
        ]>
        <book>
            <title> JSP深入编程</title>
            <author>张三</author>
        </book>

现在为文档中的元素应用了名称空间,相应地就要重写DTD,如例3-11所示。

例3-11

        <?xml version="1.0" encoding="GB2312"?>
        <!DOCTYPE book [
        <!ELEMENT book (bk:title, bk:author)>
        <!ATTLIST book xmlns:bk CDATA #REQUIRED>
        <!ELEMENT bk:title (#PCDATA)>
        <!ELEMENT bk:author (#PCDATA)>
        ]>
        <book xmlns:bk="http://www.sunxin.org/bk">
            <bk:title> JSP深入编程</bk:title>
            <bk:author>张三</bk:author>
        </book>

如果读者对于使用了名称空间的文档要重写DTD还有什么疑问的话,最简单的方法就是忘掉名称空间,将文档看成标准的XML文档,只不过这个文档有一些包含了冒号的元素和属性名称,然后根据这个文档,重新编写它的DTD。

读者也许会问,如果我的文档使用了名称空间就要重写DTD,那岂不是非常麻烦。实际上,这也是名称空间饱受争议的地方。因为名称空间声明中的URI只是用于提供一个唯一的名字,并不是标识用于验证的DTD文件,何况它还可以指向其他类型的资源。为了验证文档的有效性,仍然是通过查找在文档类型声明中的内部或外部的DTD进行验证。当然,为了最大限度地减少对DTD的修改,我们可以使用默认的名称空间,如例3-12所示。

例3-12

        <?xml version="1.0" encoding="GB2312"?>
        <!DOCTYPE book [
        <!ELEMENT book (title, author)>
        <!ATTLIST book xmlns CDATA #REQUIRED>
        <!ELEMENT title (#PCDATA)>
        <!ELEMENT author (#PCDATA)>
        ]>
        <book xmlns="http://www.sunxin.org/bk">
            <title></title>
            <author></author>
        </book>

这样,我们只需要在DTD中,为元素book添加一个xmlns属性的声明就可以了。但是默认的名称空间并不能完全解决冲突,如果另一个DTD将book元素定义为包含#PCDATA,那么XML处理器在进行有效性验证的时候,就会看到两个一样的元素book却有两种定义,于是判定文档是无效的,在这种情况下,就只能通过在文档和DTD中添加前缀以区分两个不同的book元素。

3.4 小结

本章主要介绍了XML名称空间,主要内容包括:

● 为了解决多个XML文档命名冲突的问题,可以利用W3C发布的另一个推荐标准——XML名称空间。

• 在声明名称空间时,选择的URI不需要指向实际的内容,在URI所标识的位置上,可以不存在任何东西。在名称空间声明中的URI,只是形式上的标识符,其唯一的目的是提供一个唯一的名字。在名称空间声明时,不能使用相对URI,而要使用绝对URI。

• 来自于XML名称空间的名字可以作为限定名(qualified name)出现,限定名包含了一个以冒号(:)分隔的名称空间前缀和一个本地部分。要注意,没有冒号的名字也可以是限定名(在默认名称空间的情况下)。

• 默认名称空间声明中的URI可以设为空字符串,在声明范围内,没有前缀的元素将被认为不存在于任何的名称空间中。

一个属性要想在某个名称空间中,必须给该属性加上名称空间前缀,没有前缀的属性不在任何的名称空间中(包括默认的名称空间)。即使拥有属性的元素在某个名称空间中,没有前缀的属性仍然不在该名称空间或任何其他的名称空间中。