views:

650

answers:

2

I'm trying to create a schema for a <property> element which must have a <key> sub-element, and one of <val>, <shell> or <perl> and an optional <os> or <condition>, and the order of the sub-elements doesn't matter.

Here are some sample for valid <property> elements:

<property>
  <key>A</key>
  <val>b</val>
</property>

<property>
  <key>A</key>
  <val>b</val>
  <os>Windows</os>
</property>

<property>
  <condition>a == 1</condition>
  <key>A</key>
  <perl>1+1</perl>
  <os>unix</os>
</property>

Ideally, I thought of using <xs:all> for this:

<xs:element name="property">
  <xs:complexType>
    <xs:all>
      <xs:element name="key" type="xs:string" />
      <xs:choice>
        <xs:element name="val" type="xs:string" />
        <xs:element name="perl" type="xs:string" />
        <xs:element name="shell" type="xs:string" />
      </xs:choice>
      <xs:element name="os" type="xs:string" minOccurs="0" />
      <xs:element name="condition" type="xs:string" minOccurs="0" />
    </xs:all>
  </xs:complexType>
</xs:element>

But I found out that <xs:all> can contain only <xs:element> and not <xs:choice>. Can someone explain why is it?

More importantly, can someone provide a way to validate such a <property> element?

I can put the three elements - <val>, <perl> and <shell> - as optional elements in the <xs:all>, but I want the schema to validate that one and only one of the three exists in the element. Can this be done?

+1  A: 

Based on newt's comment about using substitution groups for the choice (tested with xmllint):

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;
  <xs:element name="property">
    <xs:complexType>
      <xs:all>
        <xs:element name="key" type="xs:string" />
        <xs:element ref="val"/>
        <xs:element name="os" type="xs:string" minOccurs="0" />
        <xs:element name="condition" type="xs:string" minOccurs="0" />
      </xs:all>
    </xs:complexType>
  </xs:element>

  <xs:element name="val" type="xs:string"/>
  <xs:element name="perl" type="xs:string" substitutionGroup="val" />
  <xs:element name="shell" type="xs:string"  substitutionGroup="val" />
</xs:schema>
13ren
Great, I didn't know about substitution in XSD schema. This seems to be doing what I need. Thanks.
splintor
+4  A: 

I think this is a bit better, as the "choice" is now it's own element (typeFacet), but cannot be used directly as it is abstract.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;
  <xs:element name="property">
    <xs:complexType>
      <xs:all>
        <xs:element name="key" type="xs:string" />
        <xs:element ref="typeFacet" />
        <xs:element name="os" type="xs:string" minOccurs="0" />
        <xs:element name="condition" type="xs:string" minOccurs="0" />
      </xs:all>
    </xs:complexType>
  </xs:element>

  <xs:element name="typeFacet" abstract="true" />
  <xs:element name="val" type="xs:string"   substitutionGroup="typeFacet" />
  <xs:element name="perl" type="xs:string" substitutionGroup="typeFacet" />
  <xs:element name="shell" type="xs:string" substitutionGroup="typeFacet" />
</xs:schema>
Adam Hawkes
yep, that's an improvement.
13ren
Yes, that's pretty well exactly what I meant but was too meetinged to actually do. Abstraction is definitely a good idea here too.
Nic Gibson
Great, I didn't know about substitution in XSD schema. This seems to be doing what I need. Thanks.
splintor