Search code examples
xmltypesxsdxsd-validationxml-validation

XSD reuse a choice of elements without wrapping it


I need to write an XSD for an XML which contains recursive expression trees like that:

<binary op="plus">
  <var>X</var>
  <const>5</const>
</binary>

where operands can always be any of var, const, call, unary, binary, so for example these are also valid:

<binary op="divide">
  <const>2</const>
  <const>2</const>
</binary>

<binary op="plus">
  <call>f</call>
  <binary op="minus">
    <var>Y</var>
    <var>Y</var>
  </binary>
</binary>

I would like to somehow define the choice among const, var, call, unary, binary in one place, to limit redundancy. I can do this with a named type, but only with an additional wrapping/nesting like:

<binary op="plus">
  <operand><call>f</call></operand>
  <operand><var>Y</var></operand>
</binary>

which is not what is required. Is it possible to define a concise XSD for the original format, that is, without the additional level of <operand /> ?


Solution

  • Use an element substitution group...

    XSD

    This XSD will successfully validate all three of your example XML documents, without operand wrapping, as requested:

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    
      <xs:element name="binary" substitutionGroup="TermSubGroup">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="TermSubGroup"
                        minOccurs="2" maxOccurs="2"/>
          </xs:sequence>
          <xs:attribute name="op" type="xs:string"/>
        </xs:complexType>
      </xs:element>
    
      <xs:element name="TermSubGroup" abstract="true"/>
    
      <xs:element name="var" type="TermGroup" substitutionGroup="TermSubGroup"/>
      <xs:element name="const" type="TermGroup" substitutionGroup="TermSubGroup"/>
      <xs:element name="call" type="TermGroup" substitutionGroup="TermSubGroup"/>
    
      <xs:simpleType name="TermGroup">
        <xs:restriction base="xs:string"/>
      </xs:simpleType>
    
    </xs:schema>