XSD validation: N children with additional, different constraints

145 views Asked by At

I have XML that I want to validate using an XSD. It's actually a simple scenario but I could not find the right answer. This is the XML:

<data>
  <point>
    <x>count</x>
    <y>218</y>
  </point>
  <point>
    <x>maxtime</x>
    <y>1</y>
  </point>
  <point>
    <x>mintime</x>
    <y>0</y>
  </point>
  <point>
    <x>mean</x>
    <y>0.11</y>
  </point>
</data>

I want to make sure that the data element contains 4 point elements and that there is only one with x element = count, only one with x = maxtime...

What I have now is the following:

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="data">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="point" type="ctPoint" minOccurs="1" maxOccurs="4" />
        </xs:sequence>
    </xs:complexType>
</xs:element>

<xs:complexType name="ctPoint">
    <xs:sequence minOccurs="1" maxOccurs="4">
        <xs:element name="x" minOccurs="1" maxOccurs="1">
            <xs:simpleType>
                <xs:restriction base="xs:string">
                    <xs:enumeration value="count" />
                    <xs:enumeration value="maxtime" />
                    <xs:enumeration value="mintime" />
                    <xs:enumeration value="mean" />
                </xs:restriction>
            </xs:simpleType>
        </xs:element>
        <xs:element name="y" type="xs:decimal" minOccurs="1" maxOccurs="1" />
    </xs:sequence>
</xs:complexType>

This validates correctly but it does not guarantee that there is only 1 count, only 1 maxtime,…

2

There are 2 answers

0
kjhughes On BEST ANSWER
I want to make sure that the data element contains 4 point elements

This constraint you can do simply with XSD 1.0's minOccurs="4" and maxOccurs="4".

and that there is only one with x element = count, only one with x = maxtime...

This constraint would have to be done out-of-band (in code) if you're stuck with XSD 1.0.

[Update: However, see @sergioFC's good XSD 1.0 idea about xs:unique if the intent is simply to have all x values be unique within data.]

If you can use XSD 1.1, as @lexicore suggested, it would work. Use xs:assert:

  <xs:element name="data">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="point" type="ctPoint" minOccurs="4" maxOccurs="4" />
      </xs:sequence>
      <xs:assert test="count(point[x = 'count']) = 1 and
                       count(point[x = 'maxtime']) = 1"/>
    </xs:complexType>
  </xs:element>
4
sergioFC On

You could use xs:uniqe which is also valid in XSD 1.0.

<xs:element name="data">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="point" type="ctPoint" minOccurs="1" maxOccurs="4" />
        </xs:sequence>
    </xs:complexType>
    <xs:unique name="myUnique">
        <!-- Select all points in data -->
        <xs:selector xpath="point" />
        <!-- The value of x of every selected point should be unique -->
        <xs:field xpath="x" />
    </xs:unique>
</xs:element>

In addition, in order to do that, I think you should change minOccurs as user kjhughes has said in his answer.