Question

Let's assume working with xml documents like this:

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

and

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

The meaning of the data is: An Item must have a price and optionally ratio for iterative price change and a maximum price that should not be exceeded. To clarify: either only {Price} or all three elements {Price, Ratio, MaxPrice}. No other option is allowed.

On a regular basis, XSD allows making elements optional using minOccurs="0", so we can define an Item like this:

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

But that is not enough. This will let e.g. an Item with only {Price and Ratio} through - which is not correct.

How can this be done?

Was it helpful?

Solution

Combination of xs:sequence and xs:choice is the key here, please bear in mind that the way to combine the two is significant (see below why). The correct way (at least based on what I have managed to find out) is to express build the XSD like this:

<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" />

That way you make the an optional choice to include a fixed sequence of elements, which is exactly what we were looking for.

(If you found what you were looking for, you can stop reading right here. What follows is merely and explanation what other approaches there seem to be and why they don't work)

One might wonder (and this was also my initial idea inspired from elsewhere) that fixed choice of two fixed sequences might work too and serve as a widely reusable pattern. As true as it may be for certain cases, this does not happen to be one of them. Observe:

<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" />

Seems fine, huh? Well, apparently not - the full item would not pass through! The validator iteratively falls into the first choice, validate the Price element and fails stating that following Ratio element is unexpected.

Well, an obvious attempt to remedy to situation presents itself - let's switch the choices:

<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" />

This appears to work fine, until you decide to extend the poor Item element (perhaps some other usage of Item element might cause trouble as well).

I have tried e.g.:

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

If you try this, you'll the DiscountedItems failing either with or without the {Ratio, MaxPrice} subelements depending how whether you switched the choices or not.

Nevertheless, this concludes that for this use-case it is way better to use the first mentioned "optional choice to include a fixed sequence". I found this the hard way, so you don't have to :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top