按属性值定义不同的子节点集

时间:2013-08-19 11:48:37

标签: xsd

我正在尝试为某些基于xml的数据库交换定义一个模式,如下所示:

<table name="foo">
 <row>
  <fooid>15</fooid>
  <fooname>some entry</fooname>
 </row>
 <row>
  <fooid>28</fooid>
  <fooname>something else</fooname>
 </row>
</table>
<table name="bar">
 <row>
  <barid>19</barid>
  <barcounter>93</barcounter>
 </row>
</table>

所以我有几个这样的表,在这些表中应该只有这些表中存在的字段。例如,barid不应出现在表foo中。 有没有办法来定义它?

1 个答案:

答案 0 :(得分:2)

是的,有两种方法。一个是简单的(并且依赖于一些人的直觉和文档),另一个更具表现力(但也不可避免地更复杂。)

简单的方法是将名称'table'和'row'替换为指示我们正在讨论的表的名称:

<table-foo>
  <row-foo>
    <fooid>28</fooid>
    <fooname>something</fooname>
  </row-foo>
  ...
</table-foo>
<table-bar>
  <row-bar>
    <barid>19</barid>
    <barcounter>93</barcounter>
  </row-bar>
  ...
</table-bar>

XSD验证(如使用DTD和Relax NG进行验证)主要基于所使用的元素名称。如果您希望两种不同的行包含不同的内容,请为它们指定两个不同的名称。因此可以声明foo-table及其后代:

<xs:element name="table-foo" substitutionGroup="tns:table">
  <xs:complexType>
    <xs:sequence>            
      <xs:element ref="tns:row-foo"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

<xs:element name="row-foo" substitutionGroup="tns:row">
  <xs:complexType>
    <xs:sequence>            
      <xs:element ref="tns:fooid"/>          
      <xs:element ref="tns:fooname"/>
    </xs:sequence>
  </xs:complexType>

  

同样对于酒吧桌和酒吧行。

然而,有时候,我们绝对必须或者真的想要捕捉“row-foo”和“row-bar”都有一些共同点的事实。它们在某些抽象本体中都是“行”,这对我们来说很重要。在这种情况下,您可以使用抽象元素来捕获规律性。

例如,这里是表格,行和单元格的简单抽象:

<xs:element name="table" 
            abstract="true" 
            type="tns:table"/>
<xs:element name="row" 
            abstract="true" 
            type="tns:row"/>
<xs:element name="cell" 
            abstract="true" 
            type="xs:anySimpleType"/>

表和行的类型很简单:

<xs:complexType name="table">
  <xs:sequence>
    <xs:element ref="tns:row" maxOccurs="unbounded"/>
  </xs:sequence>
</xs:complexType>
<xs:complexType name="row">
  <xs:sequence>
    <xs:element ref="tns:cell" maxOccurs="unbounded"/>
  </xs:sequence>
</xs:complexType>

现在,table-foo等的声明变得稍微复杂一些,因为对于每个声明,我们必须建立与我们刚刚定义的抽象的关系。元素foo表是表抽象的实例,其类型是抽象表类型的限制:

<xs:element name="table-foo" 
            substitutionGroup="tns:table">
  <xs:complexType>
    <xs:complexContent>
      <xs:restriction base="tns:table">
        <xs:sequence>            
          <xs:element ref="tns:row-foo"/>
        </xs:sequence>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

元素foo-row类似:我们通过使用substitutionGroup属性指定它是“行”,并且我们通过限制从抽象行类型派生它的复杂类型:

<xs:element name="row-foo" substitutionGroup="tns:row">
  <xs:complexType>
    <xs:complexContent>
      <xs:restriction base="tns:row">
        <xs:sequence>            
          <xs:element ref="tns:fooid"/>          
          <xs:element ref="tns:fooname"/>
        </xs:sequence>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

请注意,我们不允许在此处显示任意单元格,只是我们想要表格foo中的行的两种单元格类型。为了关闭模式,我们声明元素fooid和fooname是单元格,使用(再次)substitutionGroup。

<xs:element name="fooid" type="xs:integer" 
            substitutionGroup="tns:cell"/>
<xs:element name="fooname" type="xs:string" 
            substitutionGroup="tns:cell"/> 

可以使用相同的模式为表格栏声明一组不同的合法单元格:

<xs:element name="barid" type="xs:positiveInteger" 
            substitutionGroup="tns:cell"/>
<xs:element name="barcounter" type="xs:double" 
            substitutionGroup="tns:cell"/>

<xs:element name="table-bar" substitutionGroup="tns:table">
  <xs:complexType>
    <xs:complexContent>
      <xs:restriction base="tns:table">
        <xs:sequence>            
          <xs:element ref="tns:row-bar"/>
        </xs:sequence>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

<xs:element name="row-bar" substitutionGroup="tns:row"> 
  <xs:complexType>
    <xs:complexContent>
      <xs:restriction base="tns:row">
        <xs:sequence>            
          <xs:element ref="tns:barid"/>          
          <xs:element ref="tns:barcounter"/>
        </xs:sequence>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

您描述的情况是设计抽象元素和替换组的用例之一。其他可以在这里使用的技术(但我不会详细说明)包括:

  • 声明子类型,使用xsi:type(声明foo-table和bar-table作为类型表的限制或扩展,使用<table xsi:type="tns:foo-table">...</table><table xsi:type="tns:bar-table">...</table>来指导验证)
  • 断言(通过添加关于孙子的断言来声明扩展通用表类型的foo-table和bar-table类型 - 这是1.0中没有的XSD 1.1特性。)
  • 条件类型分配(声明table如果有name="foo"则获得一种类型,如果有name="bar"则获得不同类型 - 也是1.0中不可用的XSD 1.1功能。

也可能有其他方法可以做到。