views:

93

answers:

2

I'm trying to write an XML schema that allows XML to be expressed in the following ways:

pets can containt both cat and dog elements:

<root>
  <pets>
    <cat />
    <dog />
  </pets>
</root>

pets can contain just cat or dog elements

<root>
  <pets>
    <cat />
  </pets>
</root>
-----------
<root>
  <pets>
    <dog />
  </pets>
</root>

if pets has no sub elements, then it should be absent:

<root>
</root>

The best schema that I've come up with to satisfy these conditions is this:

<xs:element name="pets">
  <xs:complexType>
    <xs:choice>
      <xs:sequence>
        <xs:element name="cat"/>
        <xs:element name="dog" minOccurs="0"/>
      </xs:sequence>
      <xs:sequence>
        <xs:element name="dog"/>
      </xs:sequence>
    </xs:choice>
  </xs:complexType>
</xs:element>

This has always seemed to me like too much schema for such a simple concept. Is there a simpler way to write this schema? Thanks!

+2  A: 

I think that this is ideal situation to use the xs:group element: if you wrap that around your element and make it optional (while making the contents of the element mandatory), you will get the desired effect.

<xs:group name="pets_group">
  <xs:element name="pets" minOccurs="0" maxOccurs="1">
    <xs:complexType>
      <xs:choice>
        <xs:sequence>
          <xs:element name="cat"/>
          <xs:element name="dog" minOccurs="0"/>
        </xs:sequence>
        <xs:sequence>
          <xs:element name="dog"/>
          <xs:element name="cat" minOccurs="0"/>
        </xs:sequence>
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:group>

Then instead of using the pets element, use the pets_group group:

<xs:element name="root">
  <xs:complexType>
    <xs:group ref="pets_group"/>
  </xs:complexType>
</xs:element>

Having a choice between a cat followed by an optional dog, and a dog followed by an optional cat ensures that the pets tag has something in it. The absence of the pets tag is what allows you to not specify any pet.

Alternatively, you could define them together, just by "inlining" the group reference. This is probably what you want to do if the pets tag is only used in one place.

Joshua Warner
+1  A: 

The solution you provide in your question is the most common solution. Aside from the group idea in the other answer, there is no better way. Personally, I prefer your solution.

Big standard schemas (e.g. FpML) use your pattern.

xcut