复合类型定义、元素和属性声明
在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都没有出现,也不能出现。