Search code examples
xmlxsd

XSD defining one positional mandatory element in an otherwise open list


so I searched a lot and I cant find any solution. What I need is an XSD that defines that an element must have a specific element as first or last child but can otherwise have an random order of other given elements.

Example:

<a> <!-- Valid -->
  <b />
  <c1 />
  <c2 />
  <c3 />
</a>

<a> <!-- Valid -->
  <c2 />
  <c1 />
  <c3 />
  <b />
</a>

<a> <!-- Invalid -->
  <c2 />
  <b />
  <c1 />
  <c3 />
</a>

So basically I am trying to create an XSD that allowes c1,c2,c3 in random order and in random quantity but always must have one b either at the top or the bottom.

I know about xsd:all and xsd:sequence but I can't find anything that would fit my problem.


Solution

  • With XSD 1.1 xs:all using an xs:assert:

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
        xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" vc:minVersion="1.1">
    
        <xs:element name="root">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="a" maxOccurs="unbounded">
                        <xs:complexType>
                            <xs:all>
                                <xs:element name="b" minOccurs="0" maxOccurs="1"/>
                                <xs:element name="c1"/>
                                <xs:element name="c2"/>
                                <xs:element name="c3"/>
                            </xs:all>
                            <xs:assert id="b-is-first-or-last-child"
                                test="*[1] instance of element(b) or *[last()] instance of element(b)"/>
                        </xs:complexType>                
                    </xs:element>
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    
    </xs:schema>
    

    both Saxon-EE as well as Xerces flag the last a as invalid:

    <?xml version="1.0" encoding="UTF-8"?>
    <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="schema2.xsd">
        <a> <!-- Valid -->
            <b />
            <c1 />
            <c2 />
            <c3 />
        </a>
        
        <a> <!-- Valid -->
            <c2 />
            <c1 />
            <c3 />
            <b />
        </a>
        
        <a> <!-- Invalid -->
            <c2 />
            <b />
            <c1 />
            <c3 />
        </a>
    </root>