5.1 Schema概述
XML Schema是2001年5月正式发布的W3C的推荐标准,经过数年的大规模讨论和开发如今终于尘埃落定,成为全球公认的XML环境下首选的数据建模工具。
使用DTD虽然带来较大的方便,但是,DTD存在一些缺陷:一是它用不同于XML的语言编写,需要不同的分析器技术。这增加了工具开发商的负担,降低了软件瘦身的可能性,此外开发人员需要多学一门语言及其语法。而XML Schema是按标准XML规则编写的,更容易掌握。二是DTD不支持名称空间。随着大部分的数据处理日益以XML为中心,信息的相互联系变得日益普及与深入,名称空间作用也将凸现。三是DTD在支持继承和子类方面的局限性。由于面向对象技术的出现,对继承和子类的支持已成为软件技术领域的主流概念。最后,DTD没有数据类型的概念,无法对特定元素施加数据类型,对强制性结构化无计可施,例如,如何规定名为Date的数据必须包含有效值。
这些就要依靠XML Schema了。XML Schema不仅可以定义XML文档的结构而且还允许约束文档的内容,这不同于DTD。另外,一个 XML Schema自身就是一个XML文档,其基于标签的语法比DTD中的特殊字符要清楚多了。XML Schema正是针对这些DTD的缺点而设计的,它完全使用XML作为描述手段,具有很强的描述能力、扩展能力和处理维护能力。
XML Schema是用一套预先规定的XML元素和属性创建的,这些元素和属性定义了文档的结构和内容模式。
XML Schema也是Web Services技术中需要使用的一个基本工具,然而并不是XML Schema的所有特性都会被广泛地使用,因此,本书将不对XML Schema规范做系统的介绍。目前主要有两种重要的模式:Microsoft XML Schema和W3C XML Schema,本章主要讨论W3C XML Schema。
在下面的例子中,通过使用出现在Schema元素中的名称空间声明xmlns:xsd= “http://www.w3.org/2001/XMLSchema”
,使得模式文档中的每一个元素都有一个与XML Schema名称空间相关联的名称空间前缀xsd。尽管在语法上,可以使用任意的前缀形式,但是,名称空间前缀xsd被约定用于表示XML Schema名称空间。由于使用同样的前缀,所以同样的关联就会出现在内置的简单类型的名字中,例如xsd:string。这种形式关联的目的是用来表示当前的元素或简单类型属于XML Schema语言的内置定义,而不属于模式文档作者自己的词汇表。为了在这里清楚并且简单地表示,仅提及元素的名字和简单类型名,而忽略它们的前缀xsd。
5.2 Schema的格式和使用
一个XSDL(XML Schema Definition Language)文档由元素、属性、名称空间和XML文档中的其他结点构成,并且至少要包含:schema根元素、XML模式名称空间的定义和元素定义。
5.2.1 简单实例
【例5.1】 关于书籍信息的XML文档,代码如源程序code5_1.xml所示。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
如何写这个XML文档的Schema呢?可以简单地依照它的结构来定义它的每个元素。首先加入一个xsd:schema元素。
每个Schema文档都是从schema元素开始,并且只能有一个,用以控制Schema所使用的名称空间和另外几个选项,现在的Schema标准有好几种,这里决定它所采用的标准,因此是非常重要的。
对应着XML文档的book元素,同样也定义一个名为book的元素(element)。因为这个元素有属性(attributes)和非文本的子元素(non-text children),所以认为它是一个复杂类型(complexType)。而对于简单类型,只能有值,不能有子元素或者属性。同时还注意到book元素下的子元素,都按照一定的顺序排列,因此使用顺序元素(sequence element)来描述。
顺序元素(sequence element)是一个定义子元素排列顺序的元素,在下面的章节,还将介绍其他类似的元素,如选择(choice)和全选(all)。
接着定义title和author,都是xsd:string类型的简单元素,因为没有属性(attributes)或者子元素。xsd:string是一个已经在名域中预定义了的XML Schema类型中的一个。
接着,来处理publish元素,它也是一个复杂类型,注意它的基数的定义。
同其他Schema定义语言不一样,W3C XML Schema允许定义一个元素的使用基数,能指定元素的minOccurs(最小数)和maxOccurs(最大数)。这里设置maxOccurs为unbounded,这意味着能有任意多的publish元素。这两个属性的默认值都是1。下面,用同样的方法定义其他的子元素。
下面封闭complexType和element等元素。
这样publish元素就完成了,然后封闭book的sequence元素。
现在,声明book元素的属性,通常是在最后这么做。这样做没有什么特别的原因,只不过W3C XML Schema工作组认为在元素后定义属性感觉更加自然。
最后,封闭所有剩下的元素。
至此,一个Schema已经完成了。其中,最关键的在于根据文档的上下关系来定义每一个元素和属性,并且允许有多个同名元素带有不同的内容。为了这个目的,W3C XML Schema被设计成一种有作用域的语言,它的每一个定义只能被它定义的子元素所看见。
【例5.2】 关于books.xml模式定义的完整例子,代码如源程序code5_2.xsd所示。
5.2.2 Schema的使用
符合某个模式的文档称为实例。实例可以根据特定的模式进行验证。需要声明XML文档的Schema实例名称空间(xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
),并把名称空间映射到前缀xsi。实例与模式之间有多对多的关系。一个模式可以描述多个有效的实例(通过使用不同的根元素类型来实现),同样,一个实例也可以被多个模式描述。例如一个实例可能拥有多个模式,它们有不同的验证级别。其中一个模式可能只验证结构,而另一个模式则根据数据类型来检查每一个数据项。
1. Schema的作用
Schema文档用来验证XML文档的正确性,用来判断实例是否符合模式中所描述的所有约束,涉及到检查实例中所有的元素和属性。
Schema主要检验如下内容:
(1) 验证数据的显示格式是否正确及是否超出值的范围;
(2) 验证所有必需的信息都存在;
(3) 确保不同使用者对文档理解的方式相同。
除了对XML文档的验证外,Schema文档还在一定程度上扩充实例:
(1) 为元素和属性添加默认值和固定值;
(2) 使具有简单类型的元素和属性值中的空白符规范化。
2. Schema的引用
一个模式可能由多个模式文档构成。多个模式文档通过包含或导入机制来形成模式,当其他模式文档与主模式文档具有相同的目标名称空间时,需要使用包含;当模式文档之间各自拥有不同的目标名称空间时,需要使用导入。下面的例子建立一个单独用来验证实例的模式文档。
【例5.3】 关于多个模式文档通过包含实现定义的例子,代码如源程序code5_3.xsd所示。
要验证XML文档,必须指定Schema文档的位置。模式的位置可以利用带有名称空间模式的xsi:schemaLocation属性以及不带名称空间XML模式的xsi:noNamespace SchemaLocation 属性来指定,它们位于根/顶级元素中或XML文档的任何其他元素中。
当Schema文档不包括targetNamespace属性时,应当通过XML文档根元素的noNamespace SchemaLocation属性及W3C的Schema实例名称空间(xmlns:xsi=“http://www.w3.org /2001/XML Scheam-instance”
)来引用XML Schema文件。针对上面的实例修改如下:
然而,如果Schema文档包含了一个targetNamespace 属性,在XML文档中就将通过schemaLocation属性而不是noNamespaceSchemaLocation属性来引用Schema文档。而且,这个属性所指定的值必须是完整的。它需要包含以空格分开的两部分,前一部分是URI,这个URI与Schema文档的targetNamespace属性内部引用的URI是一致的;后一部分是Schema文件的完整路径及名称。另外,XML文档的根元素也必须声明Schema实例名称空间(xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
)。Schema文档如下所示:
则修改上面的实例为
5.3 Schema元素的声明
元素是创建XML文档的主要构建材料。在W3C XML Schema中,元素通过使用element元素实现。元素声明用于给元素指定元素类型名称、内容和数据类型等属性。在XSDL中,元素声明可以是全局的,也可以是局部的。
5.3.1 schema根元素
在schema文档中必须定义一个且只能定义一个schema根元素。根元素不但表明了文档类型,而且还包括模式的约束、XML模式名称空间的定义,其他名称空间的定义、版本信息、语言信息和其他一些属性。定义如下:
其中name属性指定Schema名称,也可以不需要。xmlns指定所属名称空间,紧跟在后面的xsd则是该名称空间的名称,名称空间http://www.w3.org/2001/XMLSchema被映射到xsd前缀,在后面将详细说明名称空间。
5.3.2 element元素
XSD中的元素是利用element来声明的。其中name属性是元素的名字,type属性是元素值的类型,在这里可以是XML Schema中内置的数据类型或其他类型。具体定义格式如下:
name是元素类型的名称,必须是以字母或下划线开头,而且只能够包含字母、数字、下划线、连接符及句号。type属性是必要的,说明元素的数据类型。
下面的例子定义全局元素声明author,为简单字符串类型。
与以上文档对应的有效XML文档如下:
在元素的定义中还有两个属性:minOccurs和maxOccurs。其中minOccurs定义了该元素在父元素中出现的最少次数(默认为1,值为大于等于0的整数),maxOccurs定义了该元素在父元素中出现的最多次数(默认为1,值为大于等于0的整数)。在maxOccurs中可以把值设置为unbounded,表示对元素出现的最多次数没有限制。
表示元素author的类型为string,出现的次数最少为0(就是可选),最多不限制。
一般来说,如果元素声明出现在Schema文档的顶级结构中,也就是说,它的父元素是schema,那么这些元素为全局元素。
相反,局部元素声明只出现在复杂类型定义的内部,局部元素声明只在该类型定义中使用,而不被其他复杂类型引用或在替换组中使用。不同的复杂类型,可以有相同元素类型名称的局部元素。
【例5.4】 显示title、author和price三种局部元素声明的模式文档,代码如源程序code5_4.xsd所示。
5.3.3 设置默认值和固定值
默认值和固定值通过给空元素增加值的方式来扩展实例。如果文档中存在空的元素,模式处理器根据模式文档的定义,会插入默认值或固定值。在XSDL中,默认值和固定值分别通过default和fixed属性设置。两个属性只能出现其中之一,因为它们是互斥的。
如果元素为空,就填入默认值。下例中,声明了city元素,并指定了默认值为“佚名”。
必须注意的是,元素声明中“空”的定义根据数据类型不同而有所不同。某些数据类型允许空值,包括string等。任何允许空字符串值的类型,元素都不会认为是空的,从而对于字符串类型元素,默认值不会被填充。相反,integer数据类型的空元素通常会被认为是空的,从而将填入默认值。此外,如果元素的xsi:nil属性被设置为true,那么就不会插入它的默认值。
元素的默认值行为见表5-1。
固定值与默认值在相同的情况下添加,它们的差别仅在于如果元素拥有一个值,则该值必须和固定值相等。当模式解析器确定元素值和固定值实际上是否相等时,会考虑到元素的数据类型。price元素的数据类型为integer,所以整数1的所有形式在实例中都会被接受,包括01、+1和周围包含空白符的1。相反,对于author元素具有数据类型为string,字符串“01”是无效的,因为与字符串“1”并不相等。
按照以上定义,元素的固定值行为见表5-2。
5.3.4 引用元素和替代
引用是利用element元素的ref属性实现的。主要适用于避免在文档中多次定义同一个元素,应当将经常使用的元素定义为根元素的子元素,即为全局元素,这样方便在文档的任何地方引用它。如每本书籍都有作者,其他产品也会有作者,因此可以把author属性声明为全局元素,供文档中多处引用。
【例5.5】 引用元素定义的模式文档,代码如源程序code5_5.xsd所示。
在这里还可以为某个定义的元素起一个别名,主要是利用element元素中的substitutionGroup属性实现的。
方法如下:
以上文档对应的有效XML文档如下:
或者:
5.4 Schema的属性声明
属性声明用于命名属性并使之与某个特定的简单类型相关联。在XSDL中,实现的方法是使用attribute元素。在XML Schema文档中可以按照定义元素的方法定义属性,但受限制的程度较高。它们只能是简单类型,只能包含文本,且没有子属性。属性是没有顺序的,而元素是有顺序的。
使用属性十分简练,元素的功能比属性强大,但在某些场合属性是非常有用的。通常,对于元数据使用属性,而对于数据则使用元素。如用属性描述单位、语言或元素值的时间相依性。
5.4.1 创建属性
该语句定义了一个名为isbn的属性,它的值必须是一个字符类型。
属性也分全局和局部属性。全局声明的属性是schema元素的子元素,在模式文档中必须是唯一的。复杂类型的元素,使用ref属性通过名称来引用属性。局部属性声明只出现在复杂类型定义中,它们仅能在类型定义中使用,而不能被其他类型重用。下面的例子显示了两个属性——isbn和amount 的全局声明,然后定义了一个复杂类型,使用ref属性通过名称来引用这两个属性。
use属性用于指示属性是必需的还是可选的,它出现在属性引用而不是属性声明中,因为它关系到属性在复杂类型中的出现,而不是属性本身。上面的例子定义的是全局的属性定义方式,如果要在复杂类型里声明属性,可以参照下面的例子:
上面的例子描述了isbn和amount两个属性的局部声明,它们完全出现在复杂类型的定义中。局部声明的属性名称,作用范围仅限于复杂类型内,在同一个复杂类型定义中,两个属性使用相同的限定名称是非法的。只有当属性会被多个名称空间的多个元素声明使用到时,才提倡将该属性声明为全局属性。
5.4.2 为属性指派类型
所有的属性声明都把属性指定为某种简单类型。所有的属性都具有简单类型而不是复杂类型,因为它们本身不能有子元素和属性。
属性声明有3种方式:
(1) 在属性声明中通过用type属性指定命名简单类型。它可以是内置类型,也可以是用户自定义类型。
(2) 通过指定simpleType子属性来指定匿名类型。
(3) 既没有type属性,又没有simpleType子属性,从而不指定特定类型。在这种情况下,属性的类型为anySimpleType,它可以拥有任何值,只有它是结构合理的XML文档。
下例显示4个属性的声明,采用了不同的类型指定方法。定义了grade属性并赋予gradeType类型,amount属性指定了内置类型integer,同时使用内嵌的匿名简单类型来声明bookcategory属性,anything属性没有指定特定的类型。
5.4.3 属性的默认值和固定值
对于属性来说,也可以通过默认值和固定值的方式增加未出现的属性来扩充实例。定义和扩充的方式与元素一致。在XSDL中,默认值和固定值分别通过default和fixed属性设置。两个属性只能出现其中之一,因为它们是互斥的。
如果属性在元素中默认,它的默认值将会被填入。如果属性出现,且包含任意值,它将保持该值不变。下面的例子显示了book元素的声明,它包含一个amount属性,该属性被指定了默认值。
固定值与默认值在基本一样的情况下被插入,区别在于,其值应和固定值相等。同时也会考虑属性的类型。
5.5 Schema的数据类型
W3C XML Schema可以把XML文档中的元素声明为特定的类型,准许解析器检查文档的内容及其结构。XML Schema定义了两种主要的数据类型:预定义简单类型和复杂类型。这两种数据类型之间的主要区别是复杂类型可以像数据一样包含其他元素而简单类型则只能包含数据。简单类型给予了XML Schema低级类型检查能力。
5.5.1 简单类型
元素和属性声明都可以使用简单类型来描述数据类型。
1. 简单类型的种类
原子类型(不可分割的类型,如string、integer等系统内建的类型)、列表类型、联合类型合起来统一称为简单类型。XML Schema具有低级类型检查能力,允许把元素定义为表5-3中的任何类型之一。赋予简单类型的元素具有字符类型内容,但没有子元素或属性。
(1) 原子类型具有不可分割的值,如10和large。
(2) 列表类型的值为用空格符隔开的原子值列表,如<availableSizes>10 large 2</available Sizes>
(3) 联合类型的值可以是原子值,也可以是列表值。它们的区别在于该类型的有效值集是两个或多个其他简单类型值空间的联合。如要对学生的成绩评定等级,可以定义一个联合类型,它允许的值既可以是0~100的整数,也可以是优、良、合格或不合格。
在前面一些例子中,有几个元素和属性被声明为简单类型。其中一些简单类型如 string 和integer是XML Schema中内置的类型,其他的一些则是源于(如果使用对象技术的语言就是继承)内置的类型。
除此之外,新的简单类型可以通过从现有的简单类型(内置的简单类型以及源于内置简单类型的简单类型)引出定义。通常,通过重新约束一个现有的简单类型来引出一个新的简单类型。换句话说,新类型的合法值范围是现有类型的值范围的子集。使用simpleType元素来定义和命名新的简单类型,使用restriction元素来指出现有的基类型,并且用它来标识约束值范围的细节。
假设希望建立一个新的整数类型称为myInteger,它的值范围为10000~99999。那么定义应当基于简单类型integer,然后定义它的值范围为10000~99999。为了定义myInteger,这样来约束integer的范围,示例如下:
这个例子显示了由一个基本类型定义和两个值域区间方面描述的组合,通过这3个要素对myInteger实施定义。
2. 简单类型的命名定义
简单类型既可以为命名简单类型又可以为匿名简单类型。命名简单类型总是在全局定义的,而且要求在模式的数据类型中具有唯一名称。类型的名称必须为XML无冒号名称,即必须以字母或下划线开始,只能包含字、数字母、下划线、连字符和句点。如上面的例子,简单类型名为myInteger。
这种类型的模式构造非常直截了当,但有些不实用。特别是如果定义了许多只应用一次而且包含非常少约束的类型,在这种情况下,一个类型应该能够被更简单的定义。这样的简单定义通常的形式是一个省略了名称和外部引用开销的匿名类型。
在下面的示例中,publish元素声明使用了匿名类型定义。一般来说,通过元素中是否包含“type=”这个属性可以判断匿名属性定义(或者是匿名元素定义)。如果出现无名称的类型定义,也可以认为是匿名属性(元素)定义。
- 简单类型的限制
每个简单类型都是从另一个简单类型派生而来的,则另一个简单类型称为基类型。可以从原子类型,也可以从用户定义的简单类型派生。上面的例子都从内置的原子类型派生成新的简单类型。所有新的简单类型都以某种方式限制其基类型的值空间。下面的例子从简单类型种类例子中定义的myInteger类型进一步限制。
简单类型定义时,都是通过restriction元素来定义限制,通过base属性来规定一种基类型。在restriction内,可以任何顺序指定任何面(facet),来对类型取值进一步限制。根据面的不同,这个元素或属性具有不同的有效值。XML Schema面的定义见表5-4。
对每个内置的原子类型来说,都有一套定义时可用的面。如果某个面适用于某种原子类型,那么也适用于由该类型派生的简单类型。这里有必要举例说明枚举的简单类型定义,下面的例子定义了一个简单类型category,用于说明书籍的类别。
- 列表类型
除了使用简单类型中描述的原子类型,XML Schema还定义了其他两种简单类型:列表类型和联合类型。
列表类型由一组原子类型组成,因此它的每一个部分(原子)本身都是有意义的,定义时以空格隔开原子值的列表。举例来说category是个列表类型。这个类型的元素将是NMTOKEN的列表,不同的NMTOKEN值使用空格分隔,如“小说 散文 传记”。XML Schema有3个内置的列表类型,它们是NMTOKENS、IDREFS和ENTITIES。
除了使用内置的列表类型之外,还可以通过引用现有的原子类型建立新的列表类型(不可以使用现有的列表类型来建立新的列表类型,也不能使用联合类型来建立新的列表类型)。举例来说,可以建立一个名为myInteger的列表类型,并在实例文档中使用它。其中示例的后半部分即为实例文档中与列表类型listOfMyIntType相一致的实例元素myInt。
一些用于描述的参数能够被应用到列表类型的定义中,它们是length、minLength、maxLength和enumeration。举例来说,如果想定义一个列表类型,这个列表类型正好包含了6个分类项名。首先从category类型定义一个新的列表类型,称为cateList,然后通过限制cateList导出只有3个项的threeBookCate类型。具体的定义如下:
类型为threeBookCate的元素必须有3个项,它们中的每一个项必须是一个枚举类型category的原子类型(枚举类型将在后面的章节中介绍),在示例后半部分的实例文档中就是一个具体的应用例子。
需要注意的是,从原子类型string可以导出一个列表类型,然而,在一个string中也许会带有空格,而空格在一个列表类型实例中是作为分隔符使用的,因此当在使用基类型为string的列表类型时,应当格外小心。举例来说,假设定义了一个length取值为3的列表类型,同时这个列表类型基于类型string。下面由3个元素这样组成的列表是合法的Asia Europe Africa;而由3个元素这样组成的列表是不合法的Asia Europe Latin America。即使Latin America在列表外可以作为单独的一个字符串存在,但当它包含在列表中时,在Latin和America之间的空格使得第4个项被有效地生成了,因此后面的例子不符合只有3个项的列表类型定义。
5. 联合类型
应用原子类型和列表类型,一个元素或者属性的值可以为一个或者多个原子类型(列表类型)的实例。与之相比较,一个联合类型(Union Type)包含了多个原子类型或者列表类型,而应用了联合类型的元素或属性,它们的值可以是联合类型中所包含的这些原子类型或列表类型中的任何一个类型实例。为了说明这一点,建立一个用于表示学生成绩的由评定等级或者数字列表的联合类型。gradeUnion联合类型由一个原子类型和一个列表类型构成。
组成联合类型的简单类型称为它的成员类型。成员类型必须是简单类型,不存在复杂类型的联合。除了直接用简单类型来规定成员类型外,还可以使用union元素的memberTypes属性来规定成员类型。假定已经在模式文档的其他地方定义了gradeType和scoreInteger,改写上面的例子:
当在定义一个联合类型时,union元素的memberTypes属性的值包含了联合类型中所有类型的列表。现在,假定声明了一个gradeUnion类型的元素,称为stuscore,stuscore元素有效的实例可参见上面的实例。
此外,对于联合类型而言,还有两个描述性质的参数pattern和enumeration也可以应需要使用。
5.5.2 复杂类型
复杂类型的元素拥有子元素和属性,还可以有字符内容。复杂类型和简单类型之间最根本的区别就是:复杂类型的内容中可以包含其他元素或属性,但简单类型既不能包含子元素,也不能带有任何属性。复杂类型有4种不同的内容类型:简单类型、纯元素类型、混合类型及空类型。下例中给出了具有复杂类型的元素:book是带有属性的元素、bookinfo是包含子元素的纯元素复杂类型、chapter是混合类型的复杂类型元素以及内容为空的元素price。它们分别属于下面要描述的4种不同的内容类型。
复杂类型由complexType元素创建,同简单类型一样,复杂类型可以是命名的,也可以是匿名的。命名的复杂类型可以被多个元素声明使用,一般在全局范围内定义。相反,匿名的复杂类型完全在元素声明中进行定义,而且只可以被该声明使用一次。有3种不同的方法:
(1) 单个complexContent子元素,可以使用它从一个复杂类型派生出新的复杂类型;
(2) 单个simpleContent子元素,用于从简单类型派生出复杂类型;
(3) 组(group、all、choice或sequence)加属性,用于定义一种非派生于特定类型的复杂类型。
1. 内容类型
所谓元素的内容是在标记之间的字符数据和子元素。复杂类型子元素的顺序和结构称为它的“内容模型”。复杂类型的内容类型有4种:简单内容、纯元素内容、混合内容和空内容。内容类型不依赖于属性,所有这些内容类型都允许有属性。
简单内容只允许有字符数据,它没有子元素。一般说来,简单类型和具有简单内容的复杂类型的唯一区别在于后者可以有属性,如下面的book元素。
要定义如上所示的book元素,必须按如下方法定义。复杂类型bookType的字符数据内容符合简单类型string,而且还添加了isbn属性。
纯元素内容只允许有子元素,而没有字符数据内容。下例显示了具有纯元素内容的bookinfo元素,它有两个子元素:title和author。
混合内容既允许有字符数据又允许有子元素。经常用于字母和文档等形式的自由文本。下例中显示了具有混合内容的chapter元素。chapter元素中直接包含字符数据以及子元素para。
在创建所示的chapter元素类型定义时,需要设置complexType的mixed属性为true,允许有字符数据内容。mixed的默认值为false。
空内容既不允许有字符数据也不允许有子元素。带有空内容的元素通常在属性中具有值。有某些情况下,甚至可以没有属性,但空内容的元素存在本身就有意义。如XHTML中的<br/>
元素就表示一个新行。
要定义空内容元素的类型时,只需指定属性,不用做其他内容的说明。
- 利用组合器控制结构
模式组允许把子元素声明或引用组合起来,从而构建更有意义的内容模型。模式组共有3种:sequence、choice和all。
sequence组合器,定义了一列元素并且必须按照模式中指定的顺序显示,所有子元素。如果出现的话,都必须按照该顺序出现。示例如下。
下面显示了一些book的有效实例。它的子元素都以正确的顺序出现。
choice组合器用来声明只有一个相容元素必须出现,用于互斥情况。示例如下。
下面的实例都是有效的,每一个实例包含一个符合choice组中声明的子元素。如果出现多个子元素,或者根本没有任何元素出现,那么该实例将是无效的。
choice组合器还可以允许任意数量的子元素以任意顺序出现。只要通过把choice组的maxOccurs属性设置为unbounded即可允许子元素以任何顺序出现,出现任意次数。另外,为了指定更加高级的内容,sequence和choice可以彼此嵌套,可以进行任意层数的嵌套。
all组合器用来表示符合元素声明的所有元素都应该出现(以任何顺序)且只能出现一次。all组合器与choice和sequence的不同之处在于:
(1) 只能包含元素声明和引用,而不能包含其他组。
(2) 不能出现多次。对于所包含的每个子元素,maxOccurs必须为1,而minOccurs只可以为0或1。不能出现在其他模式组中。all组必须在复杂类型的最高层。
示例如下。
根据上面的定义,下面显示了book的两个有效实例。price和publisher元素定义了minOccurs等于0,因此可以不出现。
5.6 Schema的名称空间
名称空间是XML Schema中的重要部分,它提供了一种避免元素命名冲突的方法。名称空间的用途在于为XML中使用的名称提供一个容器。
5.6.1 命名冲突
XML是可扩展的,而这种扩展不受到控制。因为XML文档中使用的元素不是固定的,那么就可能会出现多个不同的XML文档使用同一个名字来描述不同类型的元素的情况。而这种情况又往往会导致命名冲突。请看下面两个例子。
如下XML文档在table元素中携带了水果的信息:
如下XML文档在table元素中携带了桌子的信息:
如果上面两个XML文档片断碰巧在一起使用的话,那么将会出现命名冲突的情况。因为这两个片断都包含了table元素,而这两个table元素的定义与所包含的内容又各不相同。
5.6.2 使用前缀解决命名冲突问题
前面的例子中所出现的命名冲突问题就是由于XML的可扩展性导致的,没有办法能够防止任何人以与其他人不兼容的方式对文档进行扩展。名称空间的出现就是为了解决这个问题的。名称空间定义了一个标记的容器,为所有在文档中使用的标记名称提供语境。实例文档中的元素类型或属性的名称可能直接与名称空间关联,或者就属于某个名称空间。这样就可以避免命名的冲突。因此,可以这样改造上面的XML文档,在table元素前面加上前缀。
则家具table的信息变成:
现在已经没有元素命名冲突的问题了,因为这两个文档对各自的table元素使用了不同的前缀,table元素在两个文档中分别是<h:table>
和<f:table>
。但是,前缀还不能彻底解决问题,因为任何人都可以创建前缀,如果两个文档创建了相同的前缀,则又出现冲突问题。为了避免前缀冲突,可以使用如下的前缀声明。
将统一资源标识符(Uniform Resource Identifier,URI)跟前缀联系起来。因为URI跟标记或前缀不同,它具有唯一性。这就是名称空间的解决方法。
5.6.3 使用名称空间
名称空间属性一般放置在元素的开始标记处,从xmlns属性开始,后面是前缀。其使用语法格式如下:
具体示例如下:
前缀只是起着名称空间的代理作用。名称空间的名称是URI,而不是前缀,在比较两个元素时,解析器是根据URI来识别它们的名称空间,而不是根据前缀识别。上面的例子则可以与不同的名称空间关联到一起。
下面是携带了家具table的信息XML文档。
5.6.4 URI、URL和URN
名称空间的声明将一个全局名称(URI)跟元素的名称联系在一起。URI只作为标识符。仅就作为XML名称空间来说,URI不必是有效的。也就是说,它不必指向任何位置。XML名称空间只将它们作为字符串处理。比较是逐个字符进行的,因此,下面的两个URI是不同的,虽然指向同一个文档。
而且名称空间是区分大小写的。即使仅有大小写区别,也会被解析为不同的URI。
大多数的URI都是URL或Internet地址。可能指向网络上的某个文件、用户电子邮箱(mailto:branch@example.org)或者是新闻组(news:comp.exmaple.xml)。
目前正在开发另一种形式的URI,称为URN(Uniform Resource Name)。两者的区别在于:对地址来说,如果文档移动了位置,那么地址就失效了,而URN不是地址,它们与文档的位置无关。URN是用“urn:”前缀构建的,后跟一个前缀表明该URN的类型,最后是值。
urn:isbn: 0-764-58007-8
这里ISBN的编号就是URN的实例。每个ISBN编号都代表了一本书。
5.6.5 名称空间的作用域
名称空间声明可以出现在文档的任何开始标记中,它的作用域是它在其开始标记中出现的元素以及其所有子元素等。
上面的示例中声明了两个名称空间,bk是顶层元素声明的,因此对所有元素都是有效的。ph是为publisher元素声明的,只对它的子元素有效。但是,如果还有一个book元素,它的子元素publisher使用了ph前缀,则是不合法的名称空间,因为超出了其作用域。一般来说,最好把所有名称空间声明都放到根元素的开始标记中。这样可以一下子看到文档的所有名称空间,对于它们的作用域也不会混淆。
5.6.6 在XSDL中使用的名称空间
- 目标名称空间
每一个XSDL模式文档都可以是一个名称空间,这称为它的目标名称空间(Target Namespace)。每个被全局声明所声明和定义的元素、属性、类型或组等都与该目标名称空间有关。下例中声明了http://www.example.org/2005/book的目标名称空间。
而下面的例子显示相应的元素如何出现在一个实例中。
- XML Schema名称空间
由于XSDL也是XML文档,所以名称空间也适用于它。在XSDL中使用的所有元素:schema、element和simpleType,都在XML Schema名称空间中,其名称空间名称为http://www.w3.org/2001/XMLSchema。此外,内置的简单类型也在这个名称空间中。最常映射到这个名称空间的前缀是xsd或xs。如上面的例子,都采用xsd前缀。 - XML Schema实例名称空间
XML Schema实例名称空间是实例中可以出现的4个与模式相关的属性的独立名称空间,其名称空间名称为http://www.w3.org/2001/XMLSchema-instance,一般映射到前缀xsi。当在实例中使用这4个属性:nil、type、schemaLocation和noNamespaceSchemaLocation时,需要声明XML Schema实例名称空间,在实例中它们的名称必须加前缀。 - 模式文档中名称空间的声明
为了解析模式文档的引用,模式文档必须包含目标名称空间和XML Schema名称空间的声明。可以把XML Schema名称空间映射到像xsd或xs这样的前缀,从而使目标名称空间成为默认名称空间。这样由XML Schema定义就显得非常清楚了,示例如下。
另一种方法是把前缀映射到目标名称空间,并使XML Schema名称空间成为默认名称空间。下例显示了这一方法。声明中的名称本身不需要加前缀,因为它们自动成为XML Schema名称空间的一部分,唯一使用前缀的地方就是对其他组件的引用。如book元素通过限定名称引用类型BookType。如果不加前缀,那么解析器将在XML Schema名称空间中寻找BookType的定义。
有时,把前缀映射到所有名称空间也是可以的,这样整个文档也非常清晰。
原文链接:https://www.f2er.com/xml/293743.html