XSD:如何使可选(子)元素可靠?

时间:2013-03-26 16:02:44

标签: xml xsd minoccurs

让我们假设使用这样的xml文档:

<Item>
  <Price>10</Price>
  <Ratio>1.5</Ratio>
  <MaxPrice>18<MaxPrice>
</Item>

<Item>
  <Price>12</Price>
</Item>

数据的含义是:必须具有价格和可选的比率以进行迭代价格变动且最大值价格不应超过。澄清::{Price}或全部三个元素{PriceRatioMaxPrice}。 不允许其他选项。

定期使用XSD可以使用minOccurs="0"将元素设为可选元素,因此我们可以像这样定义Item

<xs:element name="Item">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="Price" type="xs:integer" />
            <xs:element name="Ratio" type="xs:float" minOccurs="0" />
            <xs:element name="MaxPrice" type="xs:integer" minOccurs="0" />
        </xs:sequence>
    </xs:complexType>
</xs:element>

但这还不够。这会让例如只有{ItemPrice}通过的Ratio - 这是不正确的。

如何做到这一点?

1 个答案:

答案 0 :(得分:0)

xs:sequencexs:choice的组合是关键,请注意将两者结合起来的方法很重要(见下文原因)。正确的方法(至少根据我设法找到的方法)来表达构建XSD:

<xs:complexType name="itemType">
    <xs:sequence>
        <xs:element name="Price" type="xs:integer" />
        <xs:choice minOccurs="0">
            <xs:sequence>
                <xs:element name="Ratio" type="xs:float" />
                <xs:element name="MaxPrice" type="xs:integer" />
            </xs:sequence>
        </xs:choice>
    </xs:sequence>
</xs:complexType>

<xs:element name="Item" type="my:itemType" />

通过这种方式,您可以选择可选项来包含固定序列元素,这正是我们所寻找的。

(如果你找到了你想要的东西,你可以在这里停止阅读。以下内容仅仅是解释其他方法似乎是什么以及为什么它们不起作用)

有人可能会怀疑(这也是我从其他地方受到启发的最初想法)固定选择两个固定序列也可能起作用并且可以作为一种广泛可重复使用的模式。对于某些情况可能是正确的,但这不是其中之一。观察:

<xs:complexType name="itemType">
    <xs:choice>
        <xs:sequence>
            <xs:element name="Price" type="xs:integer" />
        </xs:sequence>
        <xs:sequence>
            <xs:element name="Price" type="xs:integer" />
            <xs:element name="Ratio" type="xs:float" />
            <xs:element name="MaxPrice" type="xs:integer" />
        </xs:sequence>
    </xs:choice>
</xs:complexType>

<xs:element name="Item" type="my:itemType" />
好像很好,对吧?好吧,显然不是 - 完整的项目不会通过!验证器迭代地落入第一选择,验证Price元素并且未能说明跟随Ratio元素是意外的

嗯,显而易见的尝试对情况进行补救 - 让我们改变选择:

<xs:complexType>
    <xs:choice>
        <xs:sequence>
            <xs:element name="Price" type="xs:integer" />
            <xs:element name="Ratio" type="xs:float" />
            <xs:element name="MaxPrice" type="xs:integer" />
        </xs:sequence>
        <xs:sequence>
            <xs:element name="Price" type="xs:integer" />
        </xs:sequence>
    </xs:choice>
</xs:complexType>

<xs:element name="Item" type="my:itemType" />

这似乎工作正常,直到您决定扩展可怜的Item元素(也许其他一些Item元素的使用也可能导致麻烦)。

我试过了例如:

<xs:complexType name="DiscountedItem">
    <xs:complexContent>
        <xs:extension base="my:itemType">
            <xs:sequence>
                <xs:element name="Discount" type="xs:integer" />
            </xs:sequence>
        </xs:extension>
    </xs:complexContent>
</xs:complexType>

如果您尝试这样做,那么DiscountedItem的{​​{1}},Ratio}子元素是否会失败,这取决于您是否切换选项。

然而,这得出结论,对于这个用例,最好使用第一个提到的“可选选项来包含固定序列”。我发现这很难,所以你不必:)