匿名类型定义

 

使用XML Schema ,我们能够通过定义一系列具有名称的类型,如PurchaseOrderType 类型,然后声明一个元素,比如purchaseOrder ,通过使用"type="

 

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


<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

 

从简单类型到复合类型

 

首先考虑一下,如何声明包含了一个属性,同时拥有简单类型值的元素。在一个实例文档中,此类的元素就像下面的形式: <internationalPrice currency="EUR">423.46</internationalPrice>

 

从购买订单模式文档中的USPrice 元素声明开始:<xsd:element name="USPrice" type="decimal"/>

 

现在如何为这个元素定义添加一个属性呢? 以前说过,简单类型不能有属性,而decimal 是简单类型。因此,必须定义一个复合类型来携带属性声明 。然而,同时也想具有简单类型 decimal 的元素内容。所以最初的问题转化为:如何定义一个基于简单类型decimal 的复合类型? 答案是,从简单类型decimal 中引出一个新的复合类型( 参见下例)


<xsd:element name="internationalPrice">
  <xsd:complexType>
   <xsd:simpleContent>
    <xsd:extension base="xsd:decimal">
     <xsd:attribute name="currency" type="xsd:string"/>
    </xsd:extension>
   </xsd:simpleContent>
  </xsd:complexType>
</xsd:element>

 

使用complexType 元素来开始定义一个新的( 匿名的) 类型。为了表示新类型的内容模型只包括字符数据而没有元素,使用simpleContent 元素来实施定义。最后,通过扩展简单的decimal 类型引出新的类型。扩展包括使用标准属性声明来添加一个currency

 

混合内容

 

购买订单模式文档的构造也许会被特征化为元素包含子元素、并且最深的子元素包含字符数据。当然,XML Schema

 

为了显示这点,考虑下面的这个使用XML 表示的客户信笺的片断,该片断包含了一些购买订单相同的元素( 参见下例)


<letterBody>
<salutation>Dear Mr.<name>Robert Smith</name>.</salutation>
Your order of <quantity>1</quantity> <productName>Baby
Monitor</productName> shipped from our warehouse on
<shipDate>1999-05-21</shipDate>. ....
</letterBody>

 

请注意在元素之间的文本和他们的子元素。在这里,文本出现在元素salutation 、quantity 、productName 和shipDate 之间,这些元素都是LetterBody 的子元素。并且在letterBody 孙子元素name


<xsd:element name="letterBody">
 <xsd:complexType mixed="true">
  <xsd:sequence>
   <xsd:element name="salutation">
    <xsd:complexType mixed="true">
     <xsd:sequence>
      <xsd:element name="name" type="xsd:string"/>
     </xsd:sequence>
    </xsd:complexType>
   </xsd:element>
   <xsd:element name="quantity" type="xsd:positiveInteger"/>
   <xsd:element name="productName" type="xsd:string"/>
   <xsd:element name="shipDate" type="xsd:date" minOccurs="0"/>
   <!-- etc. -->
  </xsd:sequence>
 </xsd:complexType>
</xsd:element>

 

出现在客户信笺中的元素是使用我们先前看到的element 和complexType 元素构造来声明的,他们的类型也是用这种方法定义的( 参见上例)

 

注意到在XML Schema 中,混合模型与XML 1.0 的混合模型有着根本的区别。在XML Schema 下面的混合模型,子元素在一个实例中出现的顺序和数量必须与子元素在模型中说明的顺序和数量一致。与之相对,在XML1.0 混合模型下,出现在实例中的子元素的顺序和数量不能被限制。总而言之,XML Schema 提供了充分的混合模型的校验而XML1.0

 

空内容

 

internationalPrice 元素用属性值来传送流通的单位和价格,而不是象先前用一个属性值以及元素内容值来表示。举例来说: <internationalPrice currency="EUR" value="423.46"/>


<xsd:element name="internationalPrice">
 <xsd:complexType>
  <xsd:complexContent>
   <xsd:restriction base="xsd:anyType">
    <xsd:attribute name="currency" type="xsd:string"/>
    <xsd:attribute name="value"    type="xsd:decimal"/>
   </xsd:restriction>
  </xsd:complexContent>
 </xsd:complexType>
</xsd:element>

 

在上例中,定义了一个匿名类型,它包含的是complexContent ,即只包含子元素。ComplexContent 元素表明想要限制或者扩展一个复合类型的内容模型,并且类型为anyType 的restriction 元素声明了两个属性,而没有引入任何元素内容。使用这种方法声明的InternationalPrice

 

前述上例中的关于空内容元素的语法相对有点冗长。我们可以通过更简洁的声明方式来声明internationalPrice 元素( 参见下例)


<xsd:element name="internationalPrice">
 <xsd:complexType>
  <xsd:attribute name="currency" type="xsd:string"/>
  <xsd:attribute name="value" type="xsd:decimal"/>
 </xsd:complexType>
</xsd:element>

 

因为一个不带有simpleContent 或者complexContent 的复合类型定义,会被解释为带有类型定义为anyType 的complexContent

 

anyType

 

anyType 表示一个称为ur-type 的抽象,它是导出所有简单类型和复合类型的基类型。一个anyType 类型不以任何形式约束其包含的内容。 可以象使用其他类型一样,使用anyType ,如: <xsd:element name="anything" type="xsd:anyType"/>

 

用这个方式声明的元素是不受约束的。所以元素的值可以为423.46 ,也可以为任何其他的字符序列,或者甚至是字符和元素的混合。实际上,anyType 是默认类型

<xsd:element name="anything"/>

 

如果需要表示不受约束的元素内容,举例来说在元素包含散文,其中可能需要嵌入标签来支持国际化的表示,那么默认的声明( 无约束)

 

注释

 

为了方便其他读者和应用来理解模式文档,XML Schema 提供了三个元素用来注释。在购买订单模式文档中,在documentation 元素中放置了一个基本的模式描述和版权信息,这是放置适合人阅读的信息的推荐位置。推荐你在任何的documentation 元素中使用 xml:lang 属性来表示这些描述信息使用的语言。另一个替代的方式是,你可以通过在schema 元素中放置 xml:lang

 

我们并没有在购买订单模式文档中使用元素 appInfo ,这个元素能够用来为工具、样式表和其他应用提供信息 。在XML Schema Part 2:Datatypes 中有一个描述简单类型的模式文档,它就是一个使用appInfo 很有趣的例子。documentation 和 appInfo 是作为annotation 元素的子元素出现的,这个元素自己出现在大多数schema 构造的开头。为了显示这点,下面的例子显示了如何在元素声明开头使用annotation 元素声明,这是一个复合类型的定义( 参见下例)


<xsd:element name="internationalPrice">
 <xsd:annotation>
  <xsd:documentation xml:lang="en">
      element declared with anonymous type
  </xsd:documentation>
 </xsd:annotation>
 <xsd:complexType>
  <xsd:annotation>
   <xsd:documentation xml:lang="en">
       empty anonymous type with 2 attributes
   </xsd:documentation>
  </xsd:annotation>
  <xsd:complexContent>
   <xsd:restriction base="xsd:anyType">
    <xsd:attribute name="currency" type="xsd:string"/>
    <xsd:attribute name="value" type="xsd:decimal"/>
   </xsd:restriction>
  </xsd:complexContent>
 </xsd:complexType>
</xsd:element>


annotation 元素也会出现在其他模式构造的开头,如那些通过元素schema 、simpleType 和attribute

再如下一个Spring

<xsd:complexType name="identifiedType" abstract="true">
<xsd:annotation>
<xsd:documentation>
<![CDATA[The unique identifier for a bean. The scope of the identifier
	is the enclosing bean factory.]]> 
  </xsd:documentation>
  </xsd:annotation>
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ The unique identifier for a bean.]]> 
  </xsd:documentation>
  </xsd:annotation>
  </xsd:attribute>
</xsd:complexType>


构造内容模型

 

在购买订单模式文档中复合类型的定义都是定义了一系列的元素,这些元素在实例文档中都必须出现。在这些类型的内容模型中,单个元素的是否出现是可选的,这我们可以通过属性值 "minOccurs=0' ( 如在comment 中) 来实现。当然我们也可以通过对minOccurs 和maxOccurs 赋予不同的值来实施其他不同的约束限制。XML Schema 也支持对在内容模型中出现的元素组的约束。我们需要注意的是,约束是不能应用在属性声明上的 。XML Schema 允许定义和命名元素组。所以元素能够用来建立复合类型的内容模型,未命名的元素组也能够和命名的元素组一起被定义。他们能够被约束为他们以声明中的相同顺序序列在实例文档中出现。

 

为了显示这点,我们在购买订单模式文档中的purchaseOrderType 定义中引入两个元素组定义。这样,购买订单就可以有两种选择来描述地址:第一种是包含彼此独立的送货地址和收款地址,第二种情况则是仅包含一个简单的地址,这个地址即是送货地址也是收款地址( 参见下例)

<xsd:complexType name="PurchaseOrderType">
 <xsd:sequence>
  <xsd:choice>
   <xsd:group ref="shipAndBill"/>
   <xsd:element name="singleUSAddress" type="USAddress"/>
  </xsd:choice>
  <xsd:element ref="comment" minOccurs="0"/>
  <xsd:element name="items"  type="Items"/>
 </xsd:sequence>
 <xsd:attribute name="orderDate" type="xsd:date"/>
</xsd:complexType>
<xsd:group name="shipAndBill">
  <xsd:sequence>
    <xsd:element name="shipTo" type="USAddress"/>
    <xsd:element name="billTo" type="USAddress"/>
  </xsd:sequence>
</xsd:group>

 

对于choice 组元素而言,在实例中仅仅允许出现这个组中的一个子内容 。对于上例中的例子而言,第一个子内容是一个内部group 元素,引用以shipAndBill 命名的元素组,这个元素组由元素序列shipTo 、billTo 组成。第二个子内容为singleUSAddress 。因此,在一个实例文档中,purchaseOrder 元素必须,要么包含一个billTo 元素和一个shipTo 元素,要么包含一个singleUSAddress 元素。choice 组后面跟着的是comment 和items 元素声明。元素和组的声明都是sequence 组的子内容。这样定义的效果是comment 和items

 

通过使用all 元素定义的元素组,在组中所有的元素都可以出现一次或者更本不出现,而且他们能够以任何顺序出现。all 组被限制放在任何内容模型的顶部,此外,all 元素组的子内容必须都为独立元素( 不能有组元素) ,在all 元素定义的内容模型中的元素都不可以出现超过一次,也就是说minOccurs 和maxOccurs 允许的值为"0" 和"1" 举例来说,为了允许purchaseOrder 的子元素以任意的顺序出现,我们能够用下面的形式重新定义 purchaseOrderType( 参见下例)


<xsd:complexType name="purchaseOrderType">
  <xsd:all>
    <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:all>
  <xsd:attribute name="orderDate" type="xsd:date"/>
</xsd:complexType>


通过使用这个定义,一个comment 元素可以选择出现在purchaseOrder 中的不同位置,可以在shipTo 、billTo 和items 元素之前或者之后出现,但他只能够出现一次。而且 all 组的约束不允许我们在组外面声明一个元素,因为这样可以允许它出现超过一次。XML Schema 限制所有的all ,换句话说,下面的形式是不合法的( 参见下例)


<xsd:complexType name="PurchaseOrderType">
 <xsd:sequence>
  <xsd:all>
    <xsd:element name="shipTo" type="USAddress"/>
    <xsd:element name="billTo" type="USAddress"/>
    <xsd:element name="items"  type="Items"/>
  </xsd:all>
  <xsd:sequence>
   <xsd:element ref="comment" minOccurs="0" maxOccurs="unbounded"/>
  </xsd:sequence>
 </xsd:sequence>
 <xsd:attribute name="orderDate" type="xsd:date"/>
</xsd:complexType>

 

最后,在内容模型中被命名或未被命名的元素组( 分别由group 、choice 、sequence 、all 所表现) 可以带有minOccurs 和maxOccurs 通过使用这些特性,XML Schema 可以完全表现DTD 所能表现的功能。而且,all

 

属性组

 

item 元素的( 匿名) 类型定义添加weightKg 和shipBy 属性声明来做到这点( 参见下例)


<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"/>
   <!-- add weightKg and shipBy attributes -->
   <xsd:attribute name="weightKg" type="xsd:decimal"/>
   <xsd:attribute name="shipBy">
    <xsd:simpleType>
     <xsd:restriction base="xsd:string">
      <xsd:enumeration value="air"/>
      <xsd:enumeration value="land"/>
      <xsd:enumeration value="any"/>
     </xsd:restriction>
    </xsd:simpleType>
   </xsd:attribute>
  </xsd:complexType>
</xsd:element>

 

或者,我们可以建立一个被命名的属性组来包含所有item 元素所期望的属性,并且在item 元素声明中通过名字来引用这个属性组( 参见下例)


<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>
  <!-- attributeGroup replaces individual declarations -->
  <xsd:attributeGroup ref="ItemDelivery"/>
 </xsd:complexType>
</xsd:element>
<xsd:attributeGroup name="ItemDelivery">
  <xsd:attribute name="partNum" type="SKU" use="required"/>
  <xsd:attribute name="weightKg" type="xsd:decimal"/>
  <xsd:attribute name="shipBy">
    <xsd:simpleType>
     <xsd:restriction base="xsd:string">
      <xsd:enumeration value="air"/>
      <xsd:enumeration value="land"/>
      <xsd:enumeration value="any"/>
     </xsd:restriction>
    </xsd:simpleType>
  </xsd:attribute>
</xsd:attributeGroup>

 

通过这种方法来使用属性组,可以提高模式文档的可读性,同时也便于更新模式文档。这是因为一个属性组能够在一个地方定义和编辑,同时能够在多个定义和声明中被引用。注意到一个属性组可以包含其他属性组,同时还要注意到属性组的声明和引用必须在复合类型定义的最后。

 

空值(Nil)

 

我们再回到前面的po.xml 来,这个购买定单中购买的物品之一Lawnmower ,是没有shipDate 元素的。在我们的这个应用背景中,模式文档和实例文档的作者可能故意安排这样的缺席用来表示这个item 还没有被运出。但是,一般的来说,缺少一个元素并没有任何特别的意义:它也许表示信息不可知或者不适用或者因为其他的原因而不存在。有时常常是通过增加一个元素而不是通过缺少一个元素,来明确地表达关于未运出的物品、未知信息或者不适用信息等 。举例来说,也许想要使用一个元素来表示发送空值或者表示数据库中的空值,类似的情况可以使用XML Shema


<?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>

 

XML Schema 空值机制包括一个空值信号。换句话说,作为元素内容而言,并没有没有真正的空值,代之的是一个说明元素的内容是空值的属性。 为了显示这点,我们修改 shipDate 元素的声明,这样空值就能够被明确地告知用户了。 <xsd:element name="shipDate" type="xsd:date" nillable="true"/>

 

为了在实例文档中明确的表示shipDate 有一个空值,我们可以设置nil 属性为真:<shipDate xsi:nil="true"></shipDate>

 

nil 属性是作为XML Schema 命名空间的一部分来定义的,即"http://www.w3.org/2001/XMLSchema-instance" ,并且在实例文档中必须带有与命名空间相对应的前缀( 一般定义为xsi:) 出现。需要注意的是,空值机制仅仅适用于元素值,而不适用于属性值,一个元素有 xsi:nil="true"

 

小结

 

本文就XML Schema 的基本特性展开介绍,希望通过本文的学习可以基本了解掌握XML Schema