XML Schema是2001年5月正式发布的W3C的推荐标准,经过数年的大范围讨论和开发如今终究尘埃落定,成为全球公认的XML环境下首选的数据建模工具。
使用DTD虽然带来较大的方便,但是,DTD存在1些缺点:1是它用不同于XML的语言编写,需要不同的分析器技术。这增加了工具开发商的负担,下降了软件瘦身的可能性,另外开发人员需要多学1门语言及其语法。而XML Schema是按标准XML规则编写的,更容易掌握。2是DTD不支持名称空间。随着大部份的数据处理日趋以XML为中心,信息的相互联系变得日趋普及与深入,名称空间作用也将凸现。3是DTD在支持继承和子类方面的局限性。由于面向对象技术的出现,对继承和子类的支持已成为软件技术领域的主流概念。最后,DTD没有数据类型的概念,没法对特定元素施加数据类型,对强迫性结构化无计可施,例如,如何规定名为Date的数据必须包括有效值。
这些就要依托XML Schema了。XML Schema不但可以定义XML文档的结构而且还允许束缚文档的内容,这不同于DTD。另外,1个 XML Schema本身就是1个XML文档,其基于标签的语法比DTD中的特殊字符要清楚多了。XML Schema正是针对这些DTD的缺点而设计的,它完全使用XML作为描写手段,具有很强的描写能力、扩大能力和处理保护能力。
XML Schema是用1套预先规定的XML元素和属性创建的,这些元素和属性定义了文档的结构和内容模式。
XML Schema也是Web Services技术中需要使用的1个基本工具,但是其实不是XML Schema的所有特性都会被广泛地使用,因此,本书将不对XML Schema规范做系统的介绍。目前主要有两种重要的模式:Microsoft XML Schema和W3C XML Schema,本章主要讨论W3C XML Schema。
在下面的例子中,通过使用出现在Schema元素中的名称空间声明xmlns:xsd= “http://www.w3.org/2001/XMLSchema”,使得模式文档中的每个元素都有1个与XML Schema名称空间相干联的名称空间前缀xsd。虽然在语法上,可使用任意的前缀情势,但是,名称空间前缀xsd被约定用于表示XML Schema名称空间。由于使用一样的前缀,所以一样的关联就会出现在内置的简单类型的名字中,例如xsd:string。这类情势关联的目的是用来表示当前的元素或简单类型属于XML Schema语言的内置定义,而不属于模式文档作者自己的辞汇表。为了在这里清楚并且简单地表示,仅提及元素的名字和简单类型名,而疏忽它们的前缀xsd。
1个XSDL(XML Schema Definition Language)文档由元素、属性、名称空间和XML文档中的其他结点构成,并且最少要包括:schema根元素、XML模式名称空间的定义和元素定义。
【例5.1】 关于书籍信息的XML文档,代码如源程序code5_1.xml所示。
如何写这个XML文档的Schema呢?可以简单地依照它的结构来定义它的每一个元素。首先加入1个xsd:schema元素。
每一个Schema文档都是从schema元素开始,并且只能有1个,用以控制Schema所使用的名称空间和另外几个选项,现在的Schema标准有好几种,这里决定它所采取的标准,因此是非常重要的。
对应着XML文档的book元素,一样也定义1个名为book的元素(element)。由于这个元素有属性(attributes)和非文本的子元素(non-text children),所以认为它是1个复杂类型(complexType)。而对简单类型,只能有值,不能有子元素或属性。同时还注意到book元素下的子元素,都依照1定的顺序排列,因此使用顺序元素(sequence element)来描写。
顺序元素(sequence element)是1个定义子元素排列顺序的元素,在下面的章节,还将介绍其他类似的元素,如选择(choice)和全选(all)。
接着定义title和author,都是xsd:string类型的简单元素,由于没有属性(attributes)或子元素。xsd:string是1个已在名域中预定义了的XML Schema类型中的1个。
接着,来处理publish元素,它也是1个复杂类型,注意它的基数的定义。
同其他Schema定义语言不1样,W3C XML Schema允许定义1个元素的使用基数,能指定元素的minOccurs(最小数)和maxOccurs(最大数)。这里设置maxOccurs为unbounded,这意味着能有任意多的publish元素。这两个属性的默许值都是1。下面,用一样的方法定义其他的子元素。
下面封闭complexType和element等元素。
这样publish元素就完成了,然后封闭book的sequence元素。
现在,声明book元素的属性,通常是在最后这么做。这样做没有甚么特别的缘由,只不过W3C XML Schema工作组认为在元素后定义属性感觉更加自然。
最后,封闭所有剩下的元素。
至此,1个Schema已完成了。其中,最关键的在于根据文档的上下关系来定义每个元素和属性,并且允许有多个同名元素带有不同的内容。为了这个目的,W3C XML Schema被设计成1种有作用域的语言,它的每个定义只能被它定义的子元素所看见。
【例5.2】 关于books.xml模式定义的完全例子,代码如源程序code5_2.xsd所示。
符合某个模式的文档称为实例。实例可以根据特定的模式进行验证。需要声明XML文档的Schema实例名称空间(xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”),并把名称空间映照到前缀xsi。实例与模式之间有多对多的关系。1个模式可以描写多个有效的实例(通过使用不同的根元素类型来实现),一样,1个实例也能够被多个模式描写。例如1个实例可能具有多个模式,它们有不同的验证级别。其中1个模式可能只验证结构,而另外一个模式则根据数据类型来检查每个数据项。
1. Schema的作用
Schema文档用来验证XML文档的正确性,用来判断实例是不是符合模式中所描写的所有束缚,触及到检查实例中所有的元素和属性。
Schema主要检验以下内容:
(1) 验证数据的显示格式是不是正确及是不是超越值的范围;
(2) 验证所有必须的信息都存在;
(3) 确保不同使用者对文档理解的方式相同。
除对XML文档的验证外,Schema文档还在1定程度上扩充实例:
(1) 为元素和属性添加默许值和固定值;
(2) 使具有简单类型的元素和属性值中的空白符规范化。
2. Schema的援用
1个模式可能由多个模式文档构成。多个模式文档通过包括或导入机制来构成模式,当其他模式文档与主模式文档具有相同的目标名称空间时,需要使用包括;当模式文档之间各自具有不同的目标名称空间时,需要使用导入。下面的例子建立1个单独用来验证实例的模式文档。
【例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文档包括了1个targetNamespace 属性,在XML文档中就将通过schemaLocation属性而不是noNamespaceSchemaLocation属性来援用Schema文档。而且,这个属性所指定的值必须是完全的。它需要包括以空格分开的两部份,前1部份是URI,这个URI与Schema文档的targetNamespace属性内部援用的URI是1致的;后1部份是Schema文件的完全路径及名称。另外,XML文档的根元素也必须声明Schema实例名称空间(xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”)。Schema文档以下所示:
则修改上面的实例为
元素是创建XML文档的主要构建材料。在W3C XML Schema中,元素通过使用element元素实现。元素声明用于给元素指定元素类型名称、内容和数据类型等属性。在XSDL中,元素声明可以是全局的,也能够是局部的。
在schema文档中必须定义1个且只能定义1个schema根元素。根元素不但表明了文档类型,而且还包括模式的束缚、XML模式名称空间的定义,其他名称空间的定义、版本信息、语言信息和其他1些属性。定义以下:
其中name属性指定Schema名称,也能够不需要。xmlns指定所属名称空间,紧跟在后面的xsd则是该名称空间的名称,名称空间http://www.w3.org/2001/XMLSchema被映照到xsd前缀,在后面将详细说明名称空间。
XSD中的元素是利用element来声明的。其中name属性是元素的名字,type属性是元素值的类型,在这里可以是XML Schema中内置的数据类型或其他类型。具体定义格式以下:
name是元素类型的名称,必须是以字母或下划线开头,而且只能够包括字母、数字、下划线、连接符及句号。type属性是必要的,说明元素的数据类型。
下面的例子定义全局元素声明author,为简单字符串类型。
与以上文档对应的有效XML文档以下:
在元素的定义中还有两个属性:minOccurs和maxOccurs。其中minOccurs定义了该元素在父元素中出现的最少次数(默许为1,值为大于等于0的整数),maxOccurs定义了该元素在父元素中出现的最屡次数(默许为1,值为大于等于0的整数)。在maxOccurs中可以把值设置为unbounded,表示对元素出现的最屡次数没有限制。
表示元素author的类型为string,出现的次数最少为0(就是可选),最多不限制。
1般来讲,如果元素声明出现在Schema文档的顶级结构中,也就是说,它的父元素是schema,那末这些元素为全局元素。
相反,局部元素声明只出现在复杂类型定义的内部,局部元素声明只在该类型定义中使用,而不被其他复杂类型援用或在替换组中使用。不同的复杂类型,可以有相同元素类型名称的局部元素。
【例5.4】 显示title、author和price3种局部元素声明的模式文档,代码如源程序code5_4.xsd所示。
默许值和固定值通过给空元素增加值的方式来扩大实例。如果文档中存在空的元素,模式处理器根据模式文档的定义,会插入默许值或固定值。在XSDL中,默许值和固定值分别通过default和fixed属性设置。两个属性只能出现其中之1,由于它们是互斥的。
如果元素为空,就填入默许值。下例中,声明了city元素,并指定了默许值为“佚名”。
必须注意的是,元素声明中“空”的定义根据数据类型不同而有所不同。某些数据类型允许空值,包括string等。任何允许空字符串值的类型,元素都不会认为是空的,从而对字符串类型元素,默许值不会被填充。相反,integer数据类型的空元素通常会被认为是空的,从而将填入默许值。另外,如果元素的xsi:nil属性被设置为true,那末就不会插入它的默许值。
元素的默许值行动见表5⑴。
固定值与默许值在相同的情况下添加,它们的差别仅在于如果元素具有1个值,则该值必须和固定值相等。当模式解析器肯定元素值和固定值实际上是不是相等时,会斟酌到元素的数据类型。price元素的数据类型为integer,所以整数1的所有情势在实例中都会被接受,包括01、+1和周围包括空白符的1。相反,对author元素具有数据类型为string,字符串“01”是无效的,由于与字符串“1”其实不相等。
依照以上定义,元素的固定值行动见表5⑵。
援用是利用element元素的ref属性实现的。主要适用于避免在文档中屡次定义同1个元素,应当将常常使用的元素定义为根元素的子元素,即为全局元素,这样方便在文档的任何地方援用它。如每本书籍都有作者,其他产品也会有作者,因此可以把author属性声明为全局元素,供文档中多处援用。
【例5.5】 援用元素定义的模式文档,代码如源程序code5_5.xsd所示。
在这里还可以为某个定义的元素起1个别名,主要是利用element元素中的substitutionGroup属性实现的。
方法以下:
以上文档对应的有效XML文档以下:
或:
属性声明用于命名属性并使之与某个特定的简单类型相干联。在XSDL中,实现的方法是使用attribute元素。在XML Schema文档中可以依照定义元素的方法定义属性,但受限制的程度较高。它们只能是简单类型,只能包括文本,且没有子属性。属性是没有顺序的,而元素是有顺序的。
使用属性10分简练,元素的功能比属性强大,但在某些场合属性是非常有用的。通常,对元数据使用属性,而对数据则使用元素。如用属性描写单位、语言或元素值的时间相依性。
定义属性的方法以下:
该语句定义了1个名为isbn的属性,它的值必须是1个字符类型。
属性也分全局和局部属性。全局声明的属性是schema元素的子元素,在模式文档中必须是唯1的。复杂类型的元素,使用ref属性通过名称来援用属性。局部属性声明只出现在复杂类型定义中,它们仅能在类型定义中使用,而不能被其他类型重用。下面的例子显示了两个属性——isbn和amount 的全局声明,然后定义了1个复杂类型,使用ref属性通过名称来援用这两个属性。
use属性用于唆使属性是必须的还是可选的,它出现在属性援用而不是属性声明中,由于它关系到属性在复杂类型中的出现,而不是属性本身。上面的例子定义的是全局的属性定义方式,如果要在复杂类型里声明属性,可以参照下面的例子:
上面的例子描写了isbn和amount两个属性的局部声明,它们完全出现在复杂类型的定义中。局部声明的属性名称,作用范围仅限于复杂类型内,在同1个复杂类型定义中,两个属性使用相同的限定名称是非法的。只有当属性会被多个名称空间的多个元素声明使用到时,才提倡将该属性声明为全局属性。
所有的属性声明都把属性指定为某种简单类型。所有的属性都具有简单类型而不是复杂类型,由于它们本身不能有子元素和属性。
属性声明有3种方式:
(1) 在属性声明中通过用type属性指定命名简单类型。它可以是内置类型,也能够是用户自定义类型。
(2) 通过指定simpleType子属性来指定匿名类型。
(3) 既没有type属性,又没有simpleType子属性,从而不指定特定类型。在这类情况下,属性的类型为anySimpleType,它可以具有任何值,只有它是结构公道的XML文档。
下例显示4个属性的声明,采取了不同的类型指定方法。定义了grade属性并赋予gradeType类型,amount属性指定了内置类型integer,同时使用内嵌的匿名简单类型来声明bookcategory属性,anything属性没有指定特定的类型。
对属性来讲,也能够通过默许值和固定值的方式增加未出现的属性来扩充实例。定义和扩充的方式与元素1致。在XSDL中,默许值和固定值分别通过default和fixed属性设置。两个属性只能出现其中之1,由于它们是互斥的。
如果属性在元素中默许,它的默许值将会被填入。如果属性出现,且包括任意值,它将保持该值不变。下面的例子显示了book元素的声明,它包括1个amount属性,该属性被指定了默许值。
固定值与默许值在基本1样的情况下被插入,区分在于,其值应和固定值相等。同时也会斟酌属性的类型。
W3C XML Schema可以把XML文档中的元素声明为特定的类型,准予解析器检查文档的内容及其结构。XML Schema定义了两种主要的数据类型:预定义简单类型和复杂类型。这两种数据类型之间的主要区分是复杂类型可以像数据1样包括其他元素而简单类型则只能包括数据。简单类型给予了XML Schema低级类型检查能力。
元素和属性声明都可使用简单类型来描写数据类型。
1. 简单类型的种类
原子类型(不可分割的类型,如string、integer等系统内建的类型)、列表类型、联合类型合起来统1称为简单类型。XML Schema具有低级类型检查能力,允许把元素定义为表5⑶中的任何类型之1。赋予简单类型的元素具有字符类型内容,但没有子元素或属性。
(1) 原子类型具有不可分割的值,如10和large。
(2) 列表类型的值为用空格符隔开的原子值列表,如10 large 2
(3) 联合类型的值可以是原子值,也能够是列表值。它们的区分在于该类型的有效值集是两个或多个其他简单类型值空间的联合。如要对学生的成绩评定等级,可以定义1个联合类型,它允许的值既可以是0~100的整数,也能够是优、良、合格或不合格。
在前面1些例子中,有几个元素和属性被声明为简单类型。其中1些简单类型如 string 和integer是XML Schema中内置的类型,其他的1些则是源于(如果使用对象技术的语言就是继承)内置的类型。
除此以外,新的简单类型可以通过从现有的简单类型(内置的简单类型和源于内置简单类型的简单类型)引出定义。通常,通太重新束缚1个现有的简单类型来引出1个新的简单类型。换句话说,新类型的合法值范围是现有类型的值范围的子集。使用simpleType元夙来定义和命名新的简单类型,使用restriction元夙来指出现有的基类型,并且用它来标识束缚值范围的细节。
假定希望建立1个新的整数类型称为myInteger,它的值范围为10000~99999。那末定义应当基于简单类型integer,然后定义它的值范围为10000~99999。为了定义myInteger,这样来束缚integer的范围,示例以下:
这个例子显示了由1个基本类型定义和两个值域区间方面描写的组合,通过这3个要素对myInteger实行定义。
2. 简单类型的命名定义
简单类型既可以为命名简单类型又可以为匿名简单类型。命名简单类型总是在全局定义的,而且要求在模式的数据类型中具有唯1名称。类型的名称必须为XML无冒号名称,即必须以字母或下划线开始,只能包括字、数字母、下划线、连字符和句点。如上面的例子,简单类型名为myInteger。
这类类型的模式构造非常直接了当,但有些不实用。特别是如果定义了许多只利用1次而且包括非常少束缚的类型,在这类情况下,1个类型应当能够被更简单的定义。这样的简单定义通常的情势是1个省略了名称和外部援用开消的匿名类型。
在下面的示例中,publish元素声明使用了匿名类型定义。1般来讲,通过元素中是不是包括“type=”这个属性可以判断匿名属性定义(或是匿名元素定义)。如果出现无名称的类型定义,也能够认为是匿名属性(元素)定义。
简单类型定义时,都是通过restriction元夙来定义限制,通过base属性来规定1种基类型。在restriction内,可以任何顺序指定任何面(facet),来对类型取值进1步限制。根据面的不同,这个元素或属性具有不同的有效值。XML Schema面的定义见表5⑷。
对每一个内置的原子类型来讲,都有1套定义时可用的面。如果某个面适用于某种原子类型,那末也适用于由该类型派生的简单类型。这里有必要举例说明枚举的简单类型定义,下面的例子定义了1个简单类型category,用于说明书籍的种别。
1些用于描写的参数能够被利用到列表类型的定义中,它们是length、minLength、maxLength和enumeration。举例来讲,如果想定义1个列表类型,这个列表类型正好包括了6个分类项名。首先从category类型定义1个新的列表类型,称为cateList,然后通过限制cateList导出只有3个项的threeBookCate类型。具体的定义以下:
类型为threeBookCate的元素必须有3个项,它们中的每个项必须是1个枚举类型category的原子类型(枚举类型将在后面的章节中介绍),在示例后半部份的实例文档中就是1个具体的利用例子。
需要注意的是,从原子类型string可以导出1个列表类型,但是,在1个string中或许会带有空格,而空格在1个列表类型实例中是作为分隔符使用的,因此当在使用基类型为string的列表类型时,应当格外谨慎。举例来讲,假定定义了1个length取值为3的列表类型,同时这个列表类型基于类型string。下面由3个元素这样组成的列表是合法的Asia Europe Africa;而由3个元素这样组成的列表是不合法的Asia Europe Latin America。即便Latin America在列表外可以作为单独的1个字符串存在,但当它包括在列表中时,在Latin和America之间的空格使得第4个项被有效地生成了,因尔后面的例子不符合只有3个项的列表类型定义。
5. 联合类型
利用原子类型和列表类型,1个元素或属性的值可以为1个或多个原子类型(列表类型)的实例。与之相比较,1个联合类型(Union Type)包括了多个原子类型或列表类型,而利用了联合类型的元素或属性,它们的值可以是联合类型中所包括的这些原子类型或列表类型中的任何1个类型实例。为了说明这1点,建立1个用于表示学生成绩的由评定等级或数字列表的联合类型。gradeUnion联合类型由1个原子类型和1个列表类型构成。
上一篇 HTTP详解
下一篇 android应用版本更新策略