模式文档可以被看做一个类型定义和元素声明的集合( 词汇表) ,他们的名字被归属于一个指定的命名空间,这个命名空间称为目标命名空间。 目标命名空间使我们能够从不同的词汇表中区分定义以及声明。举例来说,目标命名空间使我们能够区分在XML Schema 语言词汇表中的元素声明和在一个其他是么的假定的化学语言词汇表中的元素声明。前者使目标命名空间"http: //www.w3.org/2001/XMLSchema" 的一部分,而后者则是另外一个命名空间。
当我们想去检查一个实例文档是否与一个或者多个模式文档相符合( 通过一个称为模式校验的处理) ,我们需要确定在模式定义中声明和定义的哪些元素和属性应该被用来检查在实例文档中的元素和属性。在这个处理流程中,目标命名空间扮演了一个非常重要的角色。
模式文档的作者也有几种可选的方式来影响元素和属性的标识符如何在实例文档中表现。更特别的,通过使用明确的或者默认的命名空间前缀,作者能够决定在实例文档中出现的局部的声明元素和属性是否必须通过一个命名空间的约束验证。模式文档的作者就关于局部元素和属性的约束的选择与模式文档的结构以及针对实例文档的考虑有着很多的牵连,在后面的小节中我们就其中的几个重要方面进行阐述。
目标命名空间和未限定局部元素/ 属性
在新版本的定购订单模式文档po1.xsd 中( 相对于前面的那篇文章XML Schema Ⅰ,Ⅱ中的po.xsd) ,明确的声明了一个目标命名空间,并且指明局部定义的元素和属性必须是为无限定的。在po1.xsd 中的目标命名空间为 "http://www.example.com/PO1" ,显示为属性 targetNamespace 的值。
局部元素和属性的限定能够在全局指定,这是通过schema 元素的一对属性,elementFormDefault 和 attributeFormDefault 来实现的。而另一种方式,则是单独地在每个局部声明中使用form 属性指明。所有类似的属性值可以被设为 "unqualified" 或者"qualified" ,来指出局部声明的元素和属性是否必须为无限制的。
在po1.xsd 中,通过设定 elementFormDefault 和 attributeFormDefault 为 "unqualified" ,我们在全局的级别指明了元素和属性的限定。严格的说,这些设定并不必须,因为这两个属性的默认值就是这个。我们在这里声明他们是为了突出这个应用方式和后面我们描述的情形之间的对比:
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:po="http://www.example.com/PO1" targetNamespace="http://www.example.com/PO1" elementFormDefault="unqualified" attributeFormDefault="unqualified"> <element name="purchaseOrder" type="po:PurchaseOrderType"/> <element name="comment" type="string"/> <complexType name="PurchaseOrderType"> <sequence> <element name="shipTo" type="po:USAddress"/> <element name="billTo" type="po:USAddress"/> <element ref="po:comment" minOccurs="0"/> <!-- etc. --> </sequence> <!-- etc. --> </complexType> <complexType name="USAddress"> <sequence> <element name="name" type="string"/> <element name="street" type="string"/> <!-- etc. --> </sequence> </complexType> <!-- etc. --> </schema>
为了查看这个模式文档的目标命名空间是如何被填充的,应当依次检查每一个类型的定义和元素的声明。在schema 元素结束之前,首先定义了一个类型USAddress ,它由元素name 、street 等等组成。这种类型定义的一个结果是USAddress 类型必须包含在模式文档的目标命名空间中。然后,定义类型PurchaseOrderType ,这个类型由元素shipTo 、billTo 、comment 等等组成。 PurchaseorderType 也包括在模式文档的目标命名空间中。注意到在这三个元素声明中的类型引用是有前缀的,po:USAddress ,po:USAddress 和po:comment ,同时这个命名空间前缀是和命名空间”http://www.example.com/PO1 ”相联系的。这个命名空间与这个模式文档的目标命名空间是同一个命名空间。所以这个模式文档的处理者将知道在本模式文档中查找类型USAddress 的定义以及元素comment 的声明。当然,也可以通过使用不同的目标命名空间引用其他模式中的类型。因此,这样就可以在模式文档中重用外部的定义和声明。
在模式文档po1.xsd 的开始,声明了元素purchaseOrder 和comment ,他们包含在模式文档的目标命名空间中。 purchaseOrder 元素类型是有命名空间前缀的。同样USAddress 也是有命名空间前缀的。与之相反,comment 元素类型string 没有命名空间前缀。po1.xsd 模式文档包括一个默认的命名空间声明,因此没有命名空间前缀的类型诸如string ,元素诸如element. 和 complexType 都是与默认的命名空间”http://www.w3.org/2001/XMLSchema ”相关联的。实际上,这是XML Schema 自身的目标命名空间,所以一个po1.xsd 的处理者知道在XML Schema 中查找类型string 的定义和元素element. 的声明。现在,来审阅一下这个模式文档的目标命名空间是如何影响一个实例文档的一致性的:
<?xml version="1.0"?> <apo:purchaseOrder xmlns:apo="http://www.example.com/PO1" orderDate="1999-10-20"> <shipTo country="US"> <name>Alice Smith</name> <street>123 Maple Street</street> <!-- etc. --> </shipTo> <billTo country="US"> <name>Robert Smith</name> <street>8 Oak Avenue</street> <!-- etc. --> </billTo> <apo:comment>Hurry, my lawn is going wild!</apo:comment> <!-- etc. --> </apo:purchaseOrder>
在这个实例文档中声明了一个命名空间"http://www.example.com/PO1" 并且把它和命名空间前缀"apo:" 相联系。这个前缀是用于限定文档中的两个元素purchaseOrder 和comment 的。这个命名空间和在po1.xsd 中模式文档的目标命名空间是一致的。因此一个实例文档的处理者将知道应当在模式文档po1.xsd 中查找purchaseOrder 和comment 的声明。实际上,目标命名空间这样命名的原因就是因为对purchaseOrder 和comment 存在一个目标的命名空间。在模式文档中的目标命名空间从而控制了在实例文档中相应命名空间的校验。
前缀"apo:" 被应用于全局元素purchaseOrder 和comment 。此外,elementFormDefault 和attributeFormDefault 要求这个命名空间前缀不是用在局部声明的每一个元素中,如shipTo 、billTo 、name 和street ,并且也不是用在任何一个属性中( 属性都是局部声明的) 。purchaseOrder 和comment 都是全局元素,因为他们整体上都是作为schema 元素的直接子元素而存在的,而不是在一个特殊类型的内容中声明的。举例来说,在po1.xsd 中,purchaseOrder 的声明是作为schema 元素的一个直接子元素出现的,然而shipTo 的声明是作为类型定义PurchaseOrderTYpe 的complexType 元素的子元素出现的。
当局部元素和属性不需要被限定的时候,一个实例文档的作者需要知道或多或少的关于模式文档的细节来能建立模式文档,从而校验实例文档。更特殊的,如果作者能够限定只有根元素( 如 purchaseOrder) 是全局的,那么只需要简单的限定根元素。与之相对,作者也许知道所有的元素都是全局声明的,因此实例文档中所有的元素都能够被加上命名空间前缀,也许此时会利用默认的命名空间声明。另外一方面,如果全局和局部的声明没有统一的模式,则作者需要了解模式的细节来正确的对全局的元素和属性加上合适的命名空间前缀。
限定的局部元素和属性
虽然在一开始,可以描述局部元素的限定方式,然而,元素和属性仍然是可以被要求被独立地限定。为了指定所有在模式文档中局部声明的元素必须被限定,可以通过设置elementFormDefault 的值为"qualified" 来实现 :
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:po="http://www.example.com/PO1" targetNamespace="http://www.example.com/PO1" elementFormDefault="qualified" attributeFormDefault="unqualified"> <element name="purchaseOrder" type="po:PurchaseOrderType"/> <element name="comment" type="string"/> <complexType name="PurchaseOrderType"> <!-- etc. --> </complexType> <!-- etc. --> </schema>
在这个与上述模式文档相一致的实例文档中,我们明确的限定了所有的元素:
<?xml version="1.0"?> <apo:purchaseOrder xmlns:apo="http://www.example.com/PO1" orderDate="1999-10-20"> <apo:shipTo country="US"> <apo:name>Alice Smith</apo:name> <apo:street>123 Maple Street</apo:street> <!-- etc. --> </apo:shipTo> <apo:billTo country="US"> <apo:name>Robert Smith</apo:name> <apo:street>8 Oak Avenue</apo:street> <!-- etc. --> </apo:billTo> <apo:comment>Hurry, my lawn is going wild!</apo:comment> <!-- etc. --> </apo:purchaseOrder>
当然,也可以对每个元素使用默认命名空间提供的隐含限定来代替明确的显式限定 ,这就像在下面的po2.xml 中所显示的那样:
<?xml version="1.0"?> <purchaseOrder xmlns="http://www.example.com/PO1" orderDate="1999-10-20"> <shipTo country="US"> <name>Alice Smith</name> <street>123 Maple Street</street> <!-- etc. --> </shipTo> <billTo country="US"> <name>Robert Smith</name> <street>8 Oak Avenue</street> <!-- etc. --> </billTo> <comment>Hurry, my lawn is going wild!</comment> <!-- etc. --> </purchaseOrder>
在po2.xml 中,实例文档中的所有的元素都属于一个命名空间,并且命名空间的语句声明了一个默认的命名空间,这个命名空间应用于实例文档中所有的元素。因此,不需要对任何元素使用明确的显式前缀。
属性的限定和元素的限定是非常类似的。如果属性是被声明为全局属性或者attibuteFormDefault 被设置成"qualified" 的话,那么属性就必须被限定,在实例文档中将以带命名空间前缀的方式出现。 一个具备限定的属性的例子是"xsi:nil" ,实际上,需要限定的属性必须明确的加上命名空间前缀,因为XML-Namespaces 的说明没有提供关于属性的默认命名空间的机制。 在实例文档中,属性可以以不带前缀的方式出现,属性并非一定需要要具有限定修饰,一般来说,这是典型的应用情况。
对于这个限定机制,迄今为止所描绘的已经涵盖了在特定目标命名空间中所有局部元素和属性的声明方式。同时,也能够通过依靠基本的使用form 属性声明的方式来限定一个声明 。举例来说,为了需要在实例文档中限定局部声明的属性publicKey ,可以使用下面的方法来声明:
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:po="http://www.example.com/PO1" targetNamespace="http://www.example.com/PO1" elementFormDefault="qualified" attributeFormDefault="unqualified"> <!-- etc. --> <element name="secure"> <complexType> <sequence> <!-- element declarations --> </sequence> <attribute name="publicKey" type="base64Binary" form="qualified"/> </complexType> </element> </schema>
注意到在上面这个模式文档中,对于publicKey 属性,使用了form 属性,来代替attributeFormDefault 的值。同样,form 属性也能够被用在元素声明中 。下面是一个与前述模式文档相一直的实例文档的例子:
<?xml version="1.0"?> <purchaseOrder xmlns="http://www.example.com/PO1" xmlns:po="http://www.example.com/PO1" orderDate="1999-10-20"> <!-- etc. --> <secure po:publicKey="GpM7"> <!-- etc. --> </secure> </purchaseOrder>
全局声明与局部声明
另外一种模式文档的撰写风格为:当所有的元素名在一个命名空间中是唯一的时候,那么就可以创建这样一个模式文档,在这个模式文档中所有的元素都是全局的。这种方式和在DTD 中使用的效果是一样。在下面的例子中,修改了最初的po1.xsd ,使所有的元素都是全局声明,注意到在这个例子里面忽略了 elementFormDefault 和 attributeFormDefault 属性,这是为了强调突出当只有全局元素和属性声明时,他们的值无关痛痒的。
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:po="http://www.example.com/PO1" targetNamespace="http://www.example.com/PO1"> <element name="purchaseOrder" type="po:PurchaseOrderType"/> <element name="shipTo" type="po:USAddress"/> <element name="billTo" type="po:USAddress"/> <element name="comment" type="string"/> <element name="name" type="string"/> <element name="street" type="string"/> <complexType name="PurchaseOrderType"> <sequence> <element ref="po:shipTo"/> <element ref="po:billTo"/> <element ref="po:comment" minOccurs="0"/> <!-- etc. --> </sequence> </complexType> <complexType name="USAddress"> <sequence> <element ref="po:name"/> <element ref="po:street"/> <!-- etc. --> </sequence> </complexType> <!-- etc. --> </schema>
这个" 全局" 版的po1.xsd 将正确地校验实例文档po2.xml ,如同在前面所描述过地,这个文档也能够由前面那个"qualified" 版本po1.xsd 进行校验。换句话说,两个schema 方法都能够正确验证相同的、带有默认命名空间的实例文档。从一方面来看,这两个模式文档的设计方法是相近的,当然从另一方面来看,这两个模式文档的撰写方式又有着非常大的差异。特别是,当所有的元素都被声明为全局元素时,就不能利用到局部命名的优点。举例来说,对于全局元素而言,你只能够声明一个全局元素 "title" 。然而对于局部元素而言,你能够声明局部元素"title" ,它有一个 string 类型,而且它是 "book" 的子元素。在同样的模式文档中( 具有相同的目标命名空间) ,你能够声明第二个也叫"title" 的元素,它可带有枚表值 "Mr Mrs Ms" 。
未声明的目标命名空间
在前面的XML Schema Ⅰ,Ⅱ中,使用没有声明目标命名空间的模式文档和没有声明命名空间的实例文档来阐述了XML Schema 的基本知识。那么此时,自然而然地会产生一个问题,在这些模式文档和实例文档的例子里面目标命名空间是什么并且他们引用哪里?
在购买订单模式文档po.xsd 中,我们没有为模式文档声明一个目标命名空间,我们也没有声明与模式文档目标命名空间相关联的命名空间前缀( 如上面的 "po:") ,一般来说,如果有命名空间前缀的话,我们就可以在这个命名空间中查阅到模式文档中的类型和元素的定义和声明。如果在模式文档不声明目标命名空间,那么其结果就是在这个模式文档中的定义和声明,如USAddress 和purchaseOrder 都是没有命名空间修饰限定的引用。换句话说,即没有明确的命名空间前缀应用于引用,同时也没有任何默认的命名空间隐含地应用于引用。对于当前这个例子来说,purchaseOrder 元素引用了类型 PurchaseOrderType 定义来实施声明。与之相对,在po.xsd 中使用的所有XML Schema 元素和类型都是通过与XML Schema 命名空间相关联的命名空间前缀"xsd:" 所明确进行限定的。
在模式文档被设计为没有目标命名空间的场合下,我们强烈推荐所有的XML Schema 元素和类型通过一个和XML Schema 命名空间相关联的命名空间前缀如 "xsd:" 来明确实施限定 ( 如在po.xsd 中) 。这样推荐的基本原理是因为如果XML Schema 元素和类型默认地与XML Schema 命名空间相联系,比如不使用命名空间前缀,那么XML Schema 类型的引用也许不能和用户自定义类型的引用相区别。
使用一个不带有目标命名空间的模式文档中的元素声明可以去验证在实例文档中没有命名空间前缀限定的元素 。也就是说,他们可用于验证未被提供命名空间限定的元素,这些元素即没有明确的命名空间前缀,也没有默认的命名空间前缀。所以,为了验证一个传统的更本未使用命名空间的XML 1.0 文档,你必须提供一个没有目标命名空间的模式文档。当然,有很多没有使用命名空间的XML 1.0 文档,所以将会有很多没有填写目标命名空间的模式文档。
小结
本文主要针对XML Schema 中的命名空间的使用做了介绍,围绕命名空间的使用,阐述了模式的作用域以及命名空间对元素/ 属性的限定能力。在命名空间的限定下,全局元素/ 属性声明和局部元素/ 属性声明有着不同的使用方式和作用,介绍了他们在同一目标命名空间中不同定位,同时就目标命名空间的特殊使用方式作了介绍。