XML Schema帮你建模

 


XML Schema是2001年5月正式发布的W3C(万维网联盟)的推荐标准,经过数年的大规模讨论和开发如今终于奠定下来,成为全球公认的XML环境下首选的数据建模工具。 
   
  由于XML是SGML(标准通用标示语言)的一个子集,它也继承了SGML用于建模的DTD。使用DTD的好处是可以利用大量现有的DTD工具,使得开发应用代价维持在一个相对较低的水平。然而,DTD有不少缺陷: 
   
  1. DTD是基于正则表达式的,描述能力有限; 
   
  2. DTD没有数据类型的支持,在大多数应用环境下能力不足; 
   
  3. DTD的约束定义能力不足,无法对XML实例文档做出更细致的语义限制; 
   
  4. DTD不够结构化,重用的代价相对较高; 
   
  5. DTD并非使用XML作为描述手段,而DTD的构建和访问并没有标准的编程接口,无法使用标准的编程方式进行DTD维护。 
   
  XML Schema正是针对这些DTD的缺点而设计的,它完全使用XML作为描述手段,具有很强的描述能力、扩展能力和处理维护能力。 
   
   
  XML Schema简介 
   
   
  XML Schema的主要目的是用来定义一类XML文档(一个XML Application)。因此,模式的“实例文档”形式常常被用于描述一个与特定XML Schema相一致的XML文档。事实上,文档实例和Schema文档都不是必须要以文档的形式存在,它们可以以在应用之间传递的字节流的形式存在,或者作为一个数据库记录及XML的“信息项”的集合存在。然而为了简化入门,我们总是把实例和模式看作文档或者文件,认为它们总以文档实例或是模式文档的形式存在。 
   
  这里我们将结合一个实例来对XML Schema进行简单的概述,希望大家通过阅读本节的内容初步掌握的使用方法和XML Schema文档实例的具体语义。XML Schema是Web Services技术中需要使用的一个基本工具,然而并不是XML Schema的所有特性都会被广泛地使用,因此,本文将不对XML Schema规范做系统的介绍。 
   
  在介绍XML Schema语法之前,先来考虑一个XML实例文档po.xml。它描述了一个由家庭产品采购/支付应用生成的家庭产品购买订单(参阅代码1)。 
   
  

<!--代码 1 po.xml,购买订单的XML实例文档--> 
  
<?xml version="1.0"?> 
  
<purchaseOrder orderDate="1999-10-20"> 
  
<shipTo country="US"> 
  
<name>Alice Smith</name> 
  
<street>123 Maple Street</street> 
  
<city>Mill Valley</city> 
  
<state>CA</state> 
  
<zip>90952</zip> 
  
</shipTo> 
  
<billTo country="US"> 
  
<name>Robert Smith</name> 
  
<street>8 Oak Avenue</street> 
  
<city>Old Town</city> 
  
<state>PA</state> 
  
<zip>95819</zip> 
  
</billTo> 
  
<comment>Hurry, my lawn is going wild!</comment> 
  
<items> 
  
<item partNum="872-AA"> 
  
<productName>Lawnmower</productName> 
  
<quantity>1</quantity> 
  
<USPrice>148.95</USPrice> 
  
<comment>Confirm this is electric</comment> 
  
</item> 
  
<item partNum="926-AA"> 
  
<productName>Baby Monitor</productName> 
  
<quantity>1</quantity> 
  
<USPrice>39.98</USPrice> 
  
<shipDate>1999-05-21</shipDate> 
  
</item> 
  
</items> 
  
</purchaseOrder> 

   
   
   
  这个购买订单由一个跟元素purchaseOrder及其子元素shipTo、billTo、comment和items组成。这些子元素(除了comment)也依次包含了其它的一些子元素。 
   
  叶子元素包含的是一个数字而不是任何子元素,如USPrice这样的子元素。元素如果包含子元素或者是带有属性的,被称为复合类型;反之元素如果仅仅包含数字、字符串或者其它数据等,但不包含任何子元素的,则被称为简单类型。在这个实例文档中,复合类型和一些简单类型是在购买定单的模式文档中定义的,而其它一些标准的简单类型则是作为XML Schema内置的简单类型的形式出现的。 
   
  在研究这个实例文档的购买订单模式文档之前,先介绍一下购买订单实例文档和模式文档之间的联系。一个实例文档实际上并不一定需要引用模式文档,当然,在事实上的使用中,很多实例文档确实引用了模式文档,为了使入门变得更加简单,一开始我们选择不引用。同时假设任何实例文档的处理器即使从购买订单实例文档中得不到任何信息,也能够正确地进行购买订单模式文档的处理。 
   
   
  购买订单模式文档 
   
   
  在代码2,给出了购买订单的模式文档。文件po.xsd在给出解释之前,大家可以使用XML的知识去尝试理解这个模式文档。 
   
 

 <!--代码 2 po.xsd,购买订单的Schema文档--> 
  
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
  
<xsd:annotation> 
  
<xsd:documentation xml:lang="en"> 
  Purchase order schema for Example.com. 
  Copyright 2000 Example.com. All rights reserved. 
  
</xsd:documentation> 
  
</xsd:annotation> 
  
<xsd:element name="purchaseOrder" type="PurchaseOrderType"/> 
  
<xsd:element name="comment" type="xsd:string"/> 
  
<xsd:complexType name="PurchaseOrderType"> 
  
<xsd:sequence> 
  
<xsd:element name="shipTo" type="USAddress"/> 
  
<xsd:element name="billTo" type="USAddress"/> 
  
<xsd:element ref="comment" minOccurs="0"/> 
  
<xsd:element name="items" type="Items"/> 
  
</xsd:sequence> 
  
<xsd:attribute name="orderDate" type="xsd:date"/> 
  
</xsd:complexType> 
  
<xsd:complexType name="USAddress"> 
  
<xsd:sequence> 
  
<xsd:element name="name" type="xsd:string"/> 
  
<xsd:element name="street" type="xsd:string"/> 
  
<xsd:element name="city" type="xsd:string"/> 
  
<xsd:element name="state" type="xsd:string"/> 
  
<xsd:element name="zip" type="xsd:decimal"/> 
  
</xsd:sequence> 
  
<xsd:attribute name="country" type="xsd:NMTOKEN" fixed="US"/> 
  
</xsd:complexType> 
  
<xsd:complexType name="Items"> 
  
<xsd:sequence> 
  
<xsd:element name="item" minOccurs="0" maxOccurs="unbounded"> 
  
<xsd:complexType> 
  
<xsd:sequence> 
  
<xsd:element name="productName" type="xsd:string"/> 
  
<xsd:element name="quantity"> 
  
<xsd:simpleType> 
  
<xsd:restriction base="xsd:positiveInteger"> 
  
<xsd:maxExclusive value="100"/> 
  
</xsd:restriction> 
  
</xsd:simpleType> 
  
</xsd:element> 
  
<xsd:element name="USPrice" type="xsd:decimal"/> 
  
<xsd:element ref="comment" minOccurs="0"/> 
  
<xsd:element name="shipDate" type="xsd:date" minOccurs="0"/> 
  
</xsd:sequence> 
  
<xsd:attribute name="partNum" type="SKU" use="required"/> 
  
</xsd:complexType> 
  
</xsd:element> 
  
</xsd:sequence> 
  
</xsd:complexType> 
  
<!-- Stock Keeping Unit, a code for identifying products --> 
  
<xsd:simpleType name="SKU"> 
  
<xsd:restriction base="xsd:string"> 
  
<xsd:pattern value="\d{3}-[A-Z]{2}"/> 
  
</xsd:restriction> 
  
</xsd:simpleType> 
  
</xsd:schema> 
   

   
   
  购买订单模式文档由一个schema元素和一系列子元素组成,大多数子元素为element、complexType和simpleType,这些决定了实例文档中元素的表现方式和内容,大家最好能熟记element、complexType、simpleType这几个元素,这些将是我们一直需要使用的几个元素。 
   
  同时,可以通过使用出现在schema元素中的命名空间声明xmlns:xsd="http://www.w3.org/2001/XMLSchema",使得模式文档中的每一个元素都有一个与XML Schema命名空间相联系的命名空间前缀“xsd:”。尽管在语法上,可以使用任意的前缀形式,但是,命名空间前缀“xsd:”被约定用于表示XML Schema命名空间。由于使用同样的前缀,所以同样的关联就会出现在内置的简单类型的名字中,例如xsd:string。这种形式关联的目的是用来表示当前的元素或简单类型是属于XML Schema语言的内置定义的,而不是属于模式文档作者自己的词汇表的。为了在这里清楚并且简单地表示,我们仅提及元素的名字和简单类型名,而忽略它们的前缀“xsd:”。 


XML Schema帮你建模(2)

复合类型定义、元素和属性声明

  在XML Schema中,对于那些允许元素有自己的内容,以及可以携带自身属性的复合类型与那些不能够有元素内容和属性的简单类型,它们的表示形式有着本质的不同。 在实例文档中,对于能建立新的类型(无论简单和复杂)的定义和允许元素和属性有特定的名字和类型(无论是简单还是复杂)的声明,它们之间也有着显著的差别。在这一节中,将针对如何定义复合类型,以及如何声明使用复合类型的元素及其属性做较详细的描述。

  在一个模式文档中,当需要定义新的复合类型的时候,应当使用complexType元素来定义,这样的典型定义包括元素声明、元素引用和属性声明。这些元素声明与其说是它们自身的类型,不如说是一由相关模式控制的元素名与控制这些元素名在实例文档中的表现形式的型约束之间的关联。元素是通过使用element元素来声明的,属性则是通过使用attribute元素来声明的。举例来说,USAddress被定义为一个复合类型,所以在USAddress类型定义中看到它包含了五个元素的声明和一个属性的声明(参见代码3)。

 

 <!--代码3 复合类型定义示例: USAddress-->
  
<xsd:complexType name="USAddress" >
  
<xsd:sequence>
  
<xsd:element name="name" type="xsd:string"/>
  
<xsd:element name="street" type="xsd:string"/>
  
<xsd:element name="city" type="xsd:string"/>
  
<xsd:element name="state" type="xsd:string"/>
  
<xsd:element name="zip" type="xsd:decimal"/>
  
</xsd:sequence>
  
<xsd:attribute name="country" type="xsd:NMTOKEN" fixed="US"/>
  
</xsd:complexType>

 

  这个定义的含义是,在实例文档中出现任何类型声明为USAddress的元素(比如在po.xml中的shiptTo)必须包含五个元素和一个属性,而且这些元素必须被命名为name、street、city、state和zip,这些名称应该与在模式定义中element元素的name属性的值相一致。并且这些元素必须按照模式声明中的相同顺序出现,前四个元素必须包含一个字符串元素内容,第五个必须包含一个十进制数字类型的元素内容。声明为USAddress类型的元素可以带有一个country属性,该属性必须包含字符串“US”。

  USAddress类型定义仅仅包含了引用简单类型的元素声明,这些简单类型包括string、decimal 和NMTOKEN。与之对比,PurchaseOrderType类型定义(参阅代码4)则包含了引用复合类型的元素声明,如USAddress。这两个类型声明都使用同样的type属性来标识类型,而无需去区分类型是简单的还是复合的。

 

  <!--代码4 复合类型定义示例: Purchase OrderType-->
  
<xsd:complexType name="PurchaseOrderType">
  
<xsd:sequence>
  
<xsd:element name="shipTo" type="USAddress"/>
  
<xsd:element name="billTo" type="USAddress"/>
  
<xsd:element ref="comment" minOccurs="0"/>
  
<xsd:element name="items" type="Items"/>
  
</xsd:sequence>
  
<xsd:attribute name="orderDate" type="xsd:date"/>
  
</xsd:complexType>

 

 

  在PurchaseOrderType的类型定义中,对于shipTo 和 billTo这两个子元素的声明将不同的元素名与相同的复合类型相关联,这个复合类型是USAddress。这个定义的结果是,如果在实例文档中出现的任何元素(如po.xml中),当元素类型被声明为PurchaseOrderType时,那么这个元素必须包含两个名为shipTo 和billTo的元素,这两个元素都要包含五个子元素(name、street、city、state和zip),这五个子元素是作为USAddress声明的一部分出现的。根据USAddress的相关类型定义,shipTo和billTo元素也可以包含country属性。

  PurchaseOrderType类型定义包含了一个orderDate属性声明,就像在USAddress中的country属性声明一样,它被标识为一个简单类型。实际上,所有的属性声明必须引用简单类型。这是因为属性与元素声明不同,是不能包含其它元素或者属性的。

  迄今为止,我们描述的元素声明对每一个元素名都使用了一个现存的类型定义。然而有时候,使用一个现存的元素比应用一个类型来定义一个新的元素更为方便(参阅代码 5)。

 

  <!--代码 5 使用现存的元素引用来定义元素-->
  
<xsd:element ref="comment" minOccurs="0"/>

 

 

  代码5这个元素声明定义引用了一个现存的元素comment,该元素在购买订单模式文档中的其它部分被定义。一般来说,ref属性的值必须指向一个全局元素。也就是说应当是在元素下面被声明的,而不是作为复合类型定义的一部分声明的。代码5声明的含义是,一个叫comment的元素可以出现在实例文档关于这个定义的相关部分中,它的内容必须和那个被引用的元素的类型一致,在这个情况下是“string”。


  全局元素和属性


  全局的元素和全局的属性是在全局声明时建立的,全局声明都是作为元素的直接子元素出现的。一旦经过定义,全局元素或者全局属性就可以像先前描述的那样,在一个或多个元素/属性声明中使用ref属性引用。一个引用全局元素的声明,在实例文档中允许被引用的元素出现在与声明相关的元素中。所以举例来说,po.xml中的comment元素同样可以在shipTo、billTo和items元素中出现,因为引用comment的复合类型定义的声明同样出现在这三个元素的声明中。

  一个全局元素的声明也允许元素在实例文档中以顶级的文档元素出现,因此purchaseOrder元素,在po.xsd中是作为一个全局元素声明的,能够作为po.xml.中的顶级元素出现。值得注意的是,基于这个基本原理comment元素作为顶级元素出现在文档如po.xml中也是被允许的。

  关于使用全局的元素和属性有很多限制,其中一个限制是全局的声明不能够包含引用。全局的声明定义不能包含ref 属性,它们必须使用type 属性(或者像例子中使用过的跟随了一个匿名的类型定义)。第二个限制是约束不能够放在全局声明中,尽管它们能够放在引用全局声明的局部声明中。换句话说,全局声明不能够包含minOccurs、maxOccurs或者use属性。关于约束的使用,将在下一节中给出。


  出现次数约束


  我们看到,在代码5中的元素声明中minOccurs属性的值为0 ,所以comment元素在PurchaseOrderType类型中是一个可选项。一般来说,当某个元素的minOccurs的值为1或者更多的时候,该元素就必须出现。一个元素可以出现的最大数量由声明中的maxOccurs属性所决定。这个值也许是一个正的整数如1,也可能是一个很大的整数100,或者以“unbounded”的形式来表明不限最大的出现数量。minOccurs和maxOccurs属性的默认值都是1。因此,当一个元素如comment,它没有定义maxOccurs属性,那么此时该元素不可以出现超过一次。如果仅仅指定了minOccurs属性值的话,那么它必须小于等于maxOccurs的默认值,也就是说minOccurs如果单独出现,其取值只能为0或者1,这是因为minOccurs的有效值必须小于等于maxOccurs的有效值。同理,如果只指定了maxOccurs属性,它就必须大于等于minOccurs的默认值,也就是必须取值为1或者更多。如果两个属性都被省略了,那么元素必须出现且仅出现一次。

  对于属性而言,它可以有两种选择:出现一次或者根本不出现,不会有其它的出现次数。所以指定属性出现次数的语法与元素的语法有所不同。特别需要注意的,属性声明能够使用一个use属性来指明属性是否需要出现(可参见代码2,po.xsd中partNum属性的声明)。

  属性和元素的默认值都是使用default属性来声明的,不过这个属性在不同的情况下有不同的语义。当一个属性使用默认值来声明的时候,如果属性在实例文档中出现了,那么属性的值就是属性在实例文档中出现的那个值,也就是实例文档中的值是最终有效值。如果属性没有在实例文档中出现,模式处理器将认为这个属性的值等于声明中default属性的值。需要注意的是,属性默认值只在属性本身为“可选的”时候才有意义。如果在声明中,既指定了默认值,同时又设置了use属性为除“optional”以外其它值的话(也就是必须出现),处理器就会产生错误。也就是说,属性的默认值只有当属性不在实例文档中出现的时候才会生效。

  当一个元素声明中有默认值定义的时候,模式处理器在处理默认元素值的时候,与处理属性的默认值相比则有一些不同。如果实例文档中元素出现且带有自身内容的时候,元素的值就是实例文档中元素的内容。如果元素没有内容,那么模式处理器就认为这个元素的值(内容)等于声明中default属性的值。如果元素在实例文档中并不出现,则模式处理器根本不认为该元素出现。

  总而言之,元素和属性默认值之间的区别可以认为是当属性不出现时默认的属性值被应用;当元素内容为空的时候,默认的元素值被应用;而元素不出现的时候,默认的元素值不被应用。

  属性和元素声明中,都使用了fixed属性来确保属性和元素被设置为特殊的值。如po.xsd中包含了一个country属性的声明,该属性声明就有一个fixed属性,值为US。这个声明意味着在实例文档中country属性的出现是可选的(use属性的默认值是optional),但是如果属性出现它的值必须为“US”;如果属性不出现,模式处理器将自动设置country属性值为“US”。需要注意的是,fixed值的概念和default值的概念是互斥的,如果同时声明fixed和default属性就会令模式处理器产生一个错误。

  值得注意的是,在全局的元素和属性声明中,minOccurs、maxOccurs、use都没有出现,也不能出现。
 

 

 


XML Schema帮你建模(3)

 

命名冲突
  我们现在已经讨论了如何定义新的复合类型(比如PurchaseOrderType)、声明元素(比如purchaseOrder)和声明属性(如orderDate)。在这些定义行为中,一般都包含着命名,如果我们给两个对象赋予同样的名称会有何种结果? 答案取决于问题中的两个对象,一般来说这两个对象越相近,它们越有可能引起冲突。 

  下面,我们给出一些例子来说明什么时候同样的名称会导致问题。如果两个对象都是类型,而且定义了一个复合类型为USStates,同时又定义了一个简单类型为USStates,此时就出现了冲突。如果两个对象是类型和元素或者是类型和属性,当定义了一个复合类型叫USAddress时,同时又定义了一个元素称为USAddress,此时是没有冲突发生的。如果两个对象是不同类型的元素(一般地、并非是全局元素),当我们声明了一个元素名字作为USAddress类型的一部分,并且第二个元素名字作为item类型的一部分,此时就没有冲突(类似的元素有时候称为局部元素声明)。最后,如果两个对象都是类型,你自己定义了其中的一个,而XML Schema规范内置定义了另外的一个,比如定义了一个简单类型称为decimal,那么此时没有冲突发生。这里之所以没有命名冲突发生的,因为它们属于不同的命名空间。 


  使用简单类型
  在购买订单模式文档po.xsd中,有几个元素和属性被声明为简单类型。其中一些简单类型如 string 和decimal是XML Schema中内置的,其它的一些则是源于(如果使用对象技术的语言就是继承)内置的类型。举例来说,partNum属性的类型称为SKU(Stock Keeping Unit),它是源于string的。内置的简单类型和它们的后继版本都能够用在所有的元素和属性声明中。 

  新的简单类型通过从现有的简单类型(内置的简单类型以及源于内置简单类型的简单类型)引出定义。通常,我们通过重新约束一个现存的简单类型来引出一个新的简单类型。换句话说,新类型的合法值范围是现有类型的值范围的子集。我们使用simpleType元素来定义和命名新的简单类型,使用restriction元素来指出现有的基类型,并且用它来标识约束值范围的细节。 

  假设希望建立一个新的整数类型称为myInteger,它的值范围为10000到99999。那么定义应当基于简单类型integer,然后定义它的值范围为10000到99999。为了定义myInteger,这样来约束integer的范围,参见代码6:  

 

<!--代码 6 使用基类型来定义新的简单类型-->
<xsd:simpleType name="myInteger">
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="10000"/>
<xsd:maxInclusive value="99999"/>
</xsd:restriction>
</xsd:simpleType>  

 

  代码6的例子显示了由一个基本类型定义和两个值域区间方面描述的组合,通过这三个要素对myInteger实施定义。 

  先前的购买订单模式文档包含了其它更详细的定义简单类型的例子。一个叫SKU的新简单类型(参见代码7)是从(通过约束)简单类型string引出的。此外,我们使用一个称为pattern的描述,以及pattern的正则表达式值“\d{3}-[A-Z]{2}”来约束SKU的值。其中,该正则表达式值的语义为3个数字后面跟着一个连字号,接着跟着两个大写的英文字母。  

 

<!--代码7 SKU简单类型定义-->
<xsd:simpleType name="SKU">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d{3}-[A-Z]{2}"/>
</xsd:restriction>
</xsd:simpleType>  

 

 


  XML Schema定义了15个用于简单类型定义描述的元素。在这些元素中,enumeration特别有用,它能够被用于约束除boolean类型之外的几乎每一个简单类型。enumeration限制简单类型的值为一系列不同的枚举值。举例来说,我们可以使用enumeration来定义一个新类型称为USState(参见代码8),USState是从string类型引出的,同时它的值必须为美国州名的缩写。  

 

<!--代码8 USState简单类型定义-->
<xsd:simpleType name="USState">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="AK"/>
<xsd:enumeration value="AL"/>
<xsd:enumeration value="AR"/>
<!-- and so on  -->
</xsd:restriction>
</xsd:simpleType>  

 


  USState将会在现用的state元素声明中成为string类型的一个非常好的替换品。通过使用这个替换品可以使state元素具有合法值的校验能力。举例来说,billTo和shipTo元素的子元素state,将会被限制在AK、AL和AR等中。注意对于特定类型的列举值必须是惟一的。 


  匿名类型定义
  使用XML Schema,我们能够通过定义一系列具有名称的类型,如PurchaseOrderType类型。然后声明一个元素,比如purchaseOrder,通过使用“type=”这样的构造方法来应用类型。这种类型的模式构造非常直截了当,但有些不实用。特别是如果定义了许多只应用一次而且包含非常少约束的类型,在这种情况下,一个类型应该能够被更简单的定义。这样的简单定义通常的形式是一个节省了名称和外部引用开销的匿名类型。 

  在po.xsd(参见代码9)中类型Items的定义中,有两个元素声明使用了匿名类型定义,它们是item和quantity。一般的来说,你通过元素中是否包含“type=”这个属性可以判断匿名元素定义(或者是匿名属性定义)。如果出现无名称的类型定义,也可以认为是匿名元素(属性)定义。  

 

<!--代码 0-9 po.xsd模式定义-->
<xsd:complexType name="Items">
<xsd:sequence>
<xsd:element name="item" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="productName" type="xsd:string"/>
<xsd:element name="quantity">
<xsd:simpleType>
<xsd:restriction base="xsd:positiveInteger">
<xsd:maxExclusive value="100"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="USPrice" type="xsd:decimal"/>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="shipDate" type="xsd:date" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="partNum" type="SKU" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>  


  在item元素中,它被定义为一个复合匿名类型。该复杂类型是由productName、quantity、USPrice、comment、shipDate元素和一个称为partNum的属性组成的。在quantity元素中,它有一个简单匿名类型从integer类型中引出,它的值范围为1到99。 

 

 

 

XML Schema帮你建模(4)


列表类型


  除了使用简单类型中描述的原子类型(这些原子类型可用于组合成绝大多数的其它类型,包括其它的简单类型和复合类型)外,XML Schema中在简单类型范畴中还有列表类型(List Type)的概念。原子类型、列表类型以及联合类型,总称为简单类型。 一个原子类型的值在XML Schema中是不可分割的。举例来说,NMTOKEN值US是不可分割的,US的任何部分,如字母S本身都是没有意义的。与之相比较,列表类型则是由一组原子类型组成,因此它的每一个部分(原子)本身都是有意义的。举例来说NMTOKENS是个列表类型。这个类型的元素将是NMTOKEN的列表,不同的NMTOKEN值间使用空格分隔,如“US UK FR”。XML Schema有三个内置的列表类型,它们是NMTOKENS、IDREFS和ENTITIES。

  除了使用内置的列表类型之外,还可以通过引用现有的原子类型建立新的列表类型(不可以使用现有的列表类型来建立新的列表类型,也不能使用复合类型来建立新的列表类型)。举例来说,我们可以建立一个名为myInteger的列表类型,并在实例文档中使用它(参见代码10)。其中代码10中的后半部分即为实例文档中与列表类型listOfMyIntType相一致的实例元素。

 

  <!--代码 0-10 myInteger列表类型的模式定义以及实例应用-->
  
<!-- Schema Fragment -->
  
<xsd:simpleType name="listOfMyIntType">
  
<xsd:list itemType="myInteger"/>
  
</xsd:simpleType>
  
<!-- Instance Fragment -->
  
<listOfMyInt>20003 15037 95977 95945</listOfMyInt>

 

 

  一些用于描述的参数能够被应用到列表类型的定义中,它们是length、minLength、maxLength和enumeration。举例来说,如果我们想定义一个列表,这个列表正好包含了六个美国的州名(SixUSStates)。首先从USState定义一个新的列表类型,称为USStateList,然后通过限制USStateList只有六个项来导出SixUSStates。具体的定义可参见代码11。

 

  <!--代码11 列表类型USStateList及其导出的列表类型SixUSStates-->
  
<!-- Schema Fragment -->
  
<xsd:simpleType name="USStateList">
  
<xsd:list itemType="USState"/>
  
</xsd:simpleType>
  
<xsd:simpleType name="SixUSStates">
  
<xsd:restriction base="USStateList">
  
<xsd:length value="6"/>
  
</xsd:restriction>
  
</xsd:simpleType>
  
<!-- Instance Fragment -->
  
<sixStates>PA NY CA NY LA AK</sixStates>

 

 

  类型为SixUSStates的元素必须有六个项,它们中的每一个项必须是一个枚举类型USState的原子类型,在代码11后半部分的实例文档中就是一个具体的应用例子。

  我们需要注意的是,从原子类型string可以导出一个列表类型,然而,在一个string中也许会带有空格,而空格在一个列表类型实例中是作为分隔符使用的。所以当在使用基类型为string的列表类型时,应当格外小心。举例来说,假设我们定义了一个length取值为3的列表类型,同时这个列表类型是基于类型string。下面由三个元素组成的列表是合法的:“Asie Europe Afrique”;而由三个元素这样组成的列表是不合法的:“Asie Europe Amérique Latine”。即使“Amérique Latine”在列表外可以作为单独的一个字符串存在,但当它包含在列表中,在Amérique和Latine之间的空格使得第四个项被有效地生成了,因此后面的那个例子不符合只有三个项的列表类型定义。


  联合类型


  应用原子类型和列表类型,一个元素或者属性的值可以为一个或者多个原子类型(列表类型)的实例。与之相比较,一个应用联合类型(Union Type)包含了多个原子类型或者列表类型,而应用了联合类型的元素或是属性的值可以是这些原子类型或列表类型中的一个类型实例。为了显示这一点,我们建立一个用于表示美国州的、为两个字母缩写或者数字列表的联合类型。zipUnion联合类型由一个原子类型和一个列表类型构成的(参见代码12)。

 

  <!--代码 12 联合类型zipUnion及其实例-->
  
<!-- Schema Fragment -->
  
<xsd:simpleType name="zipUnion">
  
<xsd:union memberTypes="USState listOfMyIntType"/>
  
</xsd:simpleType>
  
<!-- Instance Fragment -->
  
<zips>CA</zips>
  
<zips>95630 95977 95945</zips>
  
<zips>AK</zips>

 

 

  当我们在定义一个联合类型时,元素union的memberTypes属性的值包含了联合类型中所有类型的列表。现在,假定我们声明了一个zipUnion类型的元素,称为zips,zips元素有效的实例可参见代码12。

  此外,对于联合类型而言,还有两个描述性质的参数pattern和enumeration也可以应需要使用
 

posted @ 2006-08-07 15:17  斌哥  阅读(727)  评论(0编辑  收藏  举报