分享

理解XML Schema: XML Schema进阶(I)

 thy 2009-02-23

2001 年 12 月 01 日

本文章系列将结合实例对XML Schema,这个目前国际标准的XML建模工具进行入门级别的概述,希望大家通过本系列的学习能够初步掌握XML Schema的使用方法和XML Schema文档实例的具体语义。本文主要针对XML Schema中的命名空间的使用做了介绍,围绕命名空间的使用,阐述了模式的作用域以及命名空间对元素/属性的限定能力。在命名空间的限定下,全局元素/属性声明和局部元素/属性声明有着不同的使用方式和作用,介绍了他们在同一目标命名空间中不同定位,同时就目标命名空间的特殊使用方式作了介绍。

模式文档可以被看做一个类型定义和元素声明的集合(词汇表),他们的名字被归属于一个指定的命名空间,这个命名空间称为目标命名空间。目标命名空间使我们能够从不同的词汇表中区分定义以及声明。举例来说,目标命名空间使我们能够区分在XML Schema 语言词汇表中的元素声明和在一个其他是么的假定的化学语言词汇表中的元素声明。前者使目标命名空间"http://www./2001/XMLSchema"的一部分,而后者则是另外一个命名空间。

当我们想去检查一个实例文档是否与一个或者多个模式文档相符合(通过一个称为模式校验的处理),我们需要确定在模式定义中声明和定义的哪些元素和属性应该被用来检查在实例文档中的元素和属性。在这个处理流程中,目标命名空间扮演了一个非常重要的角色。

模式文档的作者也有几种可选的方式来影响元素和属性的标识符如何在实例文档中表现。更特别的,通过使用明确的或者默认的命名空间前缀,作者能够决定在实例文档中出现的局部的声明元素和属性是否必须通过一个命名空间的约束验证。模式文档的作者就关于局部元素和属性的约束的选择与模式文档的结构以及针对实例文档的考虑有着很多的牵连,在后面的小节中我们就其中的几个重要方面进行阐述。

目标命名空间和未限定局部元素/属性

在新版本的定购订单模式文档po1.xsd中(相对于前面的那篇文章XML Schema初步中的po.xsd),我们明确的声明了一个目标命名空间,并且指明局部定义的元素和属性必须是为无限定的。在po1.xsd中的目标命名空间为"http://www./PO1",显示为属性targetNamespace的值。

局部元素和属性的限定能够在全局指定,这是通过schema元素的一对属性,elementFormDefault和attributeFormDefault来实现的。而另一种方式,则是单独地在每个局部声明中使用form属性指明。所有类似的属性值可以被设为"unqualified"或者"qualified",来指出局部声明的元素和属性是否必须为无限制的。

在po1.xsd中,通过设定elementFormDefault 和attributeFormDefault为"unqualified",我们在全局的级别指明了元素和属性的限定。严格的说,这些设定并不必须,因为这两个属性的默认值就是这个。我们在这里声明他们是为了突出这个应用方式和后面我们描述的情形之间的对比:

<schema xmlns="http://www./2001/XMLSchema"
                        xmlns:po="http://www./PO1"
                        targetNamespace="http://www./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./PO1”相联系的。这个命名空间与这个模式文档的目标命名空间是同一个命名空间。所以这个模式文档的处理者将知道在本模式文档中查找类型USAddress的定义以及元素comment的声明。当然,我们也可以通过使用不同的目标命名空间引用其他模式中的类型。因此,这样就可以在模式文档中重用外部的定义和声明。

在模式文档po1.xsd的开始,我们声明了元素purchaseOrder和comment,他们包含在模式文档的目标命名空间中。purchaseOrder元素类型是有命名空间前缀的。同样USAddress也是有命名空间前缀的。与之相反,comment元素类型、string没有命名空间前缀。po1.xsd模式文档包括一个默认的命名空间声明,因此没有命名空间前缀的类型诸如string,元素诸如element.和complexType都是与默认的命名空间”http://www./2001/XMLSchema”相关联的。实际上,这是XML Schema自身的目标命名空间,所以一个po1.xsd的处理者知道在XML Schema中查找类型string的定义和元素element.的声明。现在,让我们来审阅一下这个模式文档的目标命名空间是如何影响一个实例文档的一致性的:

<?xml version="1.0"?>
                        <apo:purchaseOrder xmlns:apo="http://www./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./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./2001/XMLSchema"
                        xmlns:po="http://www./PO1"
                        targetNamespace="http://www./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./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./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./2001/XMLSchema"
                        xmlns:po="http://www./PO1"
                        targetNamespace="http://www./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./PO1"
                        xmlns:po="http://www./PO1"
                        orderDate="1999-10-20">
                        <!-- etc. -->
                        <secure po:publicKey="GpM7">
                        <!-- etc. -->
                        </secure>
                        </purchaseOrder>
                        





回页首


全局声明与局部声明

另外一种模式文档的撰写风格为:当所有的元素名在一个命名空间中是唯一的时候,那么就可以创建这样一个模式文档,在这个模式文档中所有的元素都是全局的。这种方式和在DTD中使用的效果是一样。在下面的例子中,我们修改了最初的po1.xsd,使所有的元素都是全局声明,注意到在这个例子里面我们忽略了elementFormDefault 和 attributeFormDefault属性,这是为了强调突出当只有全局元素和属性声明时,他们的值无关痛痒的。

<schema xmlns="http://www./2001/XMLSchema"
                        xmlns:po="http://www./PO1"
                        targetNamespace="http://www./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中的命名空间的使用做了介绍,围绕命名空间的使用,阐述了模式的作用域以及命名空间对元素/属性的限定能力。在命名空间的限定下,全局元素/属性声明和局部元素/属性声明有着不同的使用方式和作用,介绍了他们在同一目标命名空间中不同定位,同时就目标命名空间的特殊使用方式作了介绍。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多