6.2 Schema的命名空间支持

前一节所介绍的两个示例文档都只是格式良好的文档,而不是有效的XML文档,因为它们既没有指定DTD作为语义约束,也没有指定Schema作为语义约束。

在XML的两种语义约束中,Schema对命名空间有极好的支持,这也是Schema比DTD更优秀的一个理由。当使用Schema定义语义约束时,XML元素和XML属性都具有明确的命名空间,意义也更加明确。

6.2.1 在Schema中使用命名空间

在定义一份Schema时可以在根元素中指定targetNamespace属性,表明该Schema定义的元素、属性和类型等Schema组件放在哪个命名空间下,以后不管是在XML文档还是在Schema中,使用这些Schema组件都必须使用命名空间。

例如下面定义了一份简单的Schema,其中就指定了targetNamespace属性:

程序清单:codes\06\6.2\namespace_Schema1.xsd

alt

上面的Schema中指定了targetNamespace="http://www.crazyit.org/schema",这表明该Schema定义的所有组件(包括元素、属性和类型等)都位于该命名空间下,不论在哪里使用这些组件都必须使用其命名空间前缀。

上面的Schema在根元素中指定了xmlns="http://www.crazyit.org/schema",这表明该命名空间的前缀为空字符串(即无须命名空间前缀),因此上面的Schema中使用<book.../>元素和item_Type类型时没有加任何前缀。

根据前面的介绍,命名空间是有意义的,它代表一个确定的范围,但命名空间对应的限定短名则没有任何意义,完全可以随意指定限定短名。因此上面的Schema可改为如下形式,其实质没有任何改变:

程序清单:codes\06\6.2\namespace_Schema2.xsd

alt

上面的Schema中的targetNamespace属性依然是http://www.crazyit.org/schema,这表明该Schema所包含的Schema组件依然处于该命名空间之下,但其根元素中指定了xmlns:crazyit="http://www.crazyit.org/schema",这意味着http://www.crazyit.org/schema命名空间对应的限定短名为crazyit。因此该Schema中使用其<book.../>元素时必须用<crazyit:book.../>的形式——其中的crazyit就是http://www.crazyit.org/schema对应的限定短名,它是可以随意指定的。因此这份Schema文档和上一个示例的Schema实质上完全相同。

此外,并不是只能在XML根元素里指定xmlns:prefix属性,XML(Schema也是XML的一种)允许为任何元素指定xmlns:prefix属性。在为某个元素指定了xmlns:prefix属性后,该元素及其所有子元素都可使用该prefix作为命名空间的限定短名。因此我们还可以将上面的Schema改为如下形式:

程序清单:codes\06\6.2\namespace_Schema3.xsd

alt

上面的Schema依然指定了targetNamespace="http://www.crazyit.org/schema",这表明该Schema内定义的所有元素和属性依然位于该命名空间之内。接着程序在①号代码处为<sequence.../>元素指定了xmlns:java="http://www.crazyit.org/schema",这意味着可使用java短名限定来代表http://www.crazyit.org/schema命名空间,因此我们可以在②号代码处使用java:book的形式引用到本Schema中的<book.../>元素。

需要指出的是:在为某个元素指定了xmlns:prefix属性之后,该属性所引入的命名空间仅对该元素及其子元素有效,因此③号代码处使用item_Type属性时依然需要使用crazyit:item_Type,而不能使用java:item_Type,这是因为③号代码没有位于<sequence…/>元素之内。

实际上,由于②号代码使用的<book…/>元素既位于<schema…/>元素之内(该元素指定了crazyit作为http://www.crazyit.org/schema的限定短名),也位于<sequence.../>元素之内(该元素指定了java作为http://www.crazyit.org/schema的限定短名),因此此处访问<book.../>元素时既可以使用crazyit:book的形式,也可以使用java:book的形式,也就是说,在②号代码处访问<book.../>元素时有多个限定短名可以使用。

实际上,我们可以在Schema的根元素中为同一个命名空间指定多个限定短名,这样可以自由地选择任意的限定短名作为访问元素、属性和类型时的前缀。也就是说,如下Schema与前面的示例Schema实质上完全相同:

程序清单:codes\06\6.2\namespace_Schema4.xsd

alt

当然,实际使用中没必要搞这么复杂,我们通常都会在XML文档的根元素里为所有可能用到的每个命名空间各指定一个限定短名,这样就可以使用每个命名空间里的元素和属性等组件了。

6.2.2 命名空间对XML文档的作用

在有些情况下,XML文档中的全部或者大部分元素都位于某个命名空间下,此时可以指定该命名空间所对应的限定短名为空字符串,这样就可以避免在XML文档中频繁地书写限定短名。

定义一份Schema时,总需要大量使用http://www.w3.org/2001/XMLSchema命名空间内的各种组件,因此可以指定空字符串作为该命名空间的限定短名,于是可以将上面的Schema文档简化为如下形式:

程序清单:codes\06\6.2\namespace_Schema5.xsd

alt

将这份Schema和前面的Schema进行对比,由于该Schema中的所有<element…/>、<complexType…/>和<sequence…/>等元素都无须使用限定短名,因此这份Schema更加简洁。


alt注意

虽然可以将一份Schema简化为上面这种形式,但由于http://www.w3.org/2001/XML Schema是各种Schema定义元素所在的命名空间,根据习惯,我们依然还是会为该命名空间指定限定短名xs或xsd。


虽然在Schema中指定某个命名空间的限定短名为空字符串不是特别合适,但在XML文档中则经常这样指定,例如下面的XML文档需要使用前面的Schema作为语义约束,而该Schema定义的元素都位于http://www.crazyit.org/schema命名空间下,于是我们就可以指定该命名空间的限定短名是空字符串,这样在使用这些元素时就无须使用任何前缀作为限定:

程序清单:codes\06\6.2\book1.xml

alt

当然我们也可以为该命名空间指定任意的限定短名,但一旦为该命名空间指定了限定短名,在后面使用这些XML元素时就必须使用这些限定短名作为前缀。如以下XML文档所示:

程序清单:codes\06\6.2\book2.xml

alt

由于可以为不同命名空间指定不同的限定短名,因此可以非常方便地在一份XML文档中使用多份Schema文档作为语义约束,这样就进一步提高了XML文档的灵活性。

例如再定义一份如下Schema:

程序清单:codes\06\6.2\favorite.xsd

alt

上面的Schema中导入了namespace_Schema1.xsd,因此这份Schema实际上使用了3个命名空间的组件:一个是http://www.w3.org/2001/XMLSchema命名空间,它的限定短名是xs;一个是http://www.crazyit.org/schema命名空间,它的限定短名是crazyit;还有一个是该Schema自身的http://www.crazyit.org/xml命名空间,它的限定短名是空字符串。因此该Schema中使用不同命名空间下的不同组件时就需要使用相应的限定短名作为前缀。

定义了上面的Schema文档后,接下来就可以在XML文档中同时使用两份Schema作为语义约束,如以下XML文档所示:

程序清单:codes\06\6.2\favorite.xml

alt

需要指出的是,如果XML文档所使用的语义约束里有一份Schema没有指定targetNamespace,则意味着该Schema没有指定命名空间,因此无法为这份Schema指定对应的限定短名,也就意味着必须为其他的Schema的命名空间都指定限定短名。假设有如下Schema:

程序清单:codes\06\6.2\favorite_nons.xsd

alt

由于没有为该Schema指定targetNamespace属性,因此使用该Schema里的元素和属性无法指定限定前缀,因而如果要在XML文档中使用其他Schema,则必须为其他Schema的命名空间指定对应的限定短名。如以下XML文档所示:

程序清单:codes\06\6.2\favorite_nons.xml

alt

通过上面的介绍不难看出,一份XML文档中最多只能有一份Schema没有指定targetNamespace属性。这是因为:当一份XML引入一份没有targetNamespace属性的Schema之后,由于这份Schema没有命名空间,因此无法为其指定相应的限定短名。如果要继续引入其他Schema作为语义约束,就必须指定不同的限定短名以示区分,这就需要后面引入的Schema必须有targetNamespace属性。

由此可见,在定义一份Schema时为其指定合适的targetNamespace属性是非常有必要的,这样才可保证更好的可扩展性。

6.2.3 为属性使用命名空间限定

通常情况下,由于属性是属于某个元素的,因此很自然地认为属性总是属于它所在元素所处的命名空间,一般无须专门为属性指定命名空间。如下Schema中增加了一个desc属性,该Schema位于http://www.crazyit.org/schema命名空间之下:

程序清单:codes\06\6.2\attribute_namespace1.xsd

alt

虽然上面的Schema中定义的XML文档位于http://www.crazyit.org/schema之下,而且下面的XML也为该命名空间指定了限定短名,但在XML文档中使用该Schema下的属性时无须使用限定短名:

程序清单:codes\06\6.2\attribute_namespace1.xml

alt

实际上完全可以在定义Schema时让某个元素包含另一个命名空间里的属性,这样在XML文档中使用该元素和属性时就需要分别指定不同的限定短名了。

如下Schema定义了一个book_Type类型,其中定义了一个属性通配符,用于表示该类型可以接受来自其他命名空间的任意属性:

程序清单:codes\06\6.2\anyAttribute.xsd

alt

上面的Schema中定义的组件都放在http://www.crazyit.org/schema命名空间下,下面再定义一份Schema,这份Schema的targetNamespace是http://www.crazyit.org/test:

程序清单:codes\06\6.2\testAttribute.xsd

alt

下面定义一份XML文档,这份XML文档同时使用上面2份Schema作为语义约束,并为<book…/>元素指定test属性,<book…/>元素和test属性不在同一个命名空间之内,因此需要指定不同的限定短名作为前缀,该XML文档如下所示:

程序清单:codes\06\6.2\attribute_namespace.xml

alt

从上面的代码可以看出,通过使用限定短名作为前缀,可以让属性和元素处于不同的命名空间之下。

6.2.4命名空间对一致性约束的影响

前面在介绍Schema时提到有3种一致性约束:key约束、keyref约束和unique约束,这3种约束对命名空间的支持有点小问题,假设有如下Schema:

程序清单:codes\06\6.2\unique_namespace.xsd

alt

上面的Schema是在<book-list…/>元素(根元素)之内定义了唯一约束,定义book-list/book之下的<name…/>元素的值必须遵守唯一约束。对于如下XML文档:

程序清单:codes\06\6.2\unique_namespace.xml

alt

如上XML文档中两个<book…/>元素里<book-name…/>子元素的值完全相同,这显然不符合唯一约束的要求,但这份XML文档依然是有效的。这是为什么呢?

发生这个问题是因为一致性约束对命名空间的支持有点小问题,上面的Schema定义一致性约束时在①、②号代码处直接使用了book、book-name两个元素,而没有使用任何前缀。本质上它们依然处于http://www.crazyit.org/schema命名空间下,只是由于指定了xmlns="http://www.crazyit.org/schema",因此在该Schema中使用时无须添加限定短名作为前缀。但一致性约束不这么处理,它的处理规律是:只要该元素没有添加任何限定短名,它就认为该元素不处在任何命名空间下。也就是说,它约束的并不是http://www.crazyit.org/schema命名空间下的book-name元素,而是对不在任何命名空间下的book-name进行约束,因此上面的XML文档自然也就有效了。

为了解决这个问题,当需要在Schema中对指定命名空间下的元素添加一致性约束时,必须为其添加限定短名作为前缀。我们可以把unique_namespace.xsd改为如下形式:

程序清单:codes\06\6.2\unique_namespace1.xsd

alt

上面的Schema中为book、book-name元素定义约束时指定了crazy前缀,于是一致性约束就会根据该前缀找到对应的命名空间,完成对http://www.crazyit.org/schema命名空间下book和book-name的唯一约束。如果上面的unique_namespace.xml改为使用这份Schema作为语义约束,那它就违反了唯一约束,自然也就是无效文档了。


alt注意

当需要在Schema中对指定命名空间下的元素(或属性)添加一致性约束时,必须为其添加限定短名作为前缀,否则一致性约束会认为只是对无命名空间的元素(或属性)添加约束。


6.2.5 局部元素和局部属性的强制限定

前面定义Schema时总是为根元素指定如下两个属性:

alt elementFormDefault="qualified"。

alt attributeFormDefault="unqualified"。

这两个属性也是与命名空间相关的,其中elementFormDefault用于指定XML文档中使用局部元素时是否必须使用限定短名作为前缀,如果该属性值为qualified,则表明必须使用限定短名作为前缀,否则可以省略限定短名。

看如下Schema定义:

程序清单:codes\06\6.2\element_unqualified.xsd

alt

上面的Schema中定义的<book-name…/>和<price…/>两个元素是局部元素,而且该Schema指定了elementFormDefault="unqualified",因此在XML文档中使用这两个元素时可以省略限定短名。例如如下XML文档所示:

程序清单:codes\06\6.2\element_unqualified.xml

alt

从上面的粗体字代码来看,XML文档中使用<book>、<book-name…/>和<price…/>等局部元素时无须添加限定短名。

必须指出的是:elementFormDefault="unqualified"仅对局部元素有效,对于Schema中的全局元素而言,不管该属性如何设置,在XML中使用时总是必须使用限定短名作为前缀。例如如下Schema所示:

程序清单:codes\06\6.2\element_unqualified2.xsd

alt

上面的Schema中<book…/>元素是一个全局元素,因此在XML文档中使用该元素时必须使用限定短名作为前缀,如以下XML文档所示:

程序清单:codes\06\6.2\element_unqualified2.xml

alt

不过需要指出的是:对于上面的XML文档,普通用户使用时很难发现<book-name…/>、<price…/>元素位于哪个命名空间下,因此这样虽然使XML文档更简洁,但却降低了其可读性。


alt注意

在Schema的根元素里指定了elementFormDefault="unqualified"之后,在XML文档中使用该Schema定义的局部元素时不能使用限定短名作为前缀,这样可以使该XML文档更简洁,但却会降低其可读性。因此通常推荐将elementFormDefault属性值设为qualified。


类似地,如果指定了attributeFormDefault="qualified",则要求XML文档中使用局部属性时必须使用限定短名作为前缀。attributeFormDefault="qualified"的设置仅对局部属性起作用,不管该属性如何设置,在XML文档中使用全局属性时总是需要添加限定短名作为前缀的。

如下Schema示范了attributeFormDefault="unqualified"的用处:

程序清单:codes\06\6.2\attribute_unqualified.xsd

alt

上面的Schema中指定了attributeFormDefault="unqualified",因此使用局部属性时无须添加限定短名作为前缀,但使用全局属性时依然必须添加限定短名作为前缀,如以下XML文档所示:

程序清单:codes\06\6.2\attribute_unqualified.xml

alt

如果在Schema中指定了attributeFormDefault="qualified",则在XML文档中使用局部属性时也必须添加限定短名作为前缀,如下Schema中就指定了attributeFormDefault="qualified":

程序清单:codes\06\6.2\attribute_unqualified2.xsd

alt

上面的Schema中指定了attributeFormDefault="qualified",这要求在XML文档中使用局部属性时也必须使用限定短名作为前缀,如以下XML文档所示:

程序清单:codes\06\6.2\attribute_unqualified2.xml

alt

需要指出的是,由于属性总是属于某个元素的,XML文档用户很容易确定该属性所处的命名空间——总是处于其所属元素所处的命名空间内,因此使用局部属性时通常无须添加限定短名作为前缀,所以Schema中通常会指定attributeFormDefault="unqualified",表明在XML文档中使用局部属性时无须添加限定短名作为前缀。