xmlxsdxsd-validation

XSD specify occurrence fits value in xml


If I have an element in an xml that is an integer representing a count n. And if I have an element with a sequence of elements that have an attribute id that should start at 1 and go up till n.

Is there some way in xsd to assure that the sequence of elements start with attribute id=1 and that it is increased by one to each next element and stops at the number specified by the value of another element?

Thanks for the Feedback so far. I will have a look at it the next week.

An minimal example of an xml could look like this:

<root>
  <meta>
    <count>3</count>
  </meta>
  <data>
    <row id="1">2.1</row>
    <row id="2">3.6</row>
    <row id="3">5.4</row>
  </data>
</root>

So the value of meta.count should match the total occurrence of row elements and those elements should start from 1 up to count value.

I tried now something like this and found out, since I can't reach siblings in the test, I need to put the test to a higher element and that works.

I can't use the value of one of the element as maxOccurs though. But probably I can create a third test for that. Edit: Yes, one can compare the count of the rows with the value of count. Thanks for the support.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
xmlns:xerces="http://xerces.apache.org" vc:minVersion="1.1">
            <xs:element name="row">
                <xs:complexType>
                    <xs:simpleContent>
                        <xs:extension base="xs:float">
                            <xs:attribute name="id" use="required" type="xs:positiveInteger"/>
                        </xs:extension>
                    </xs:simpleContent>
                </xs:complexType>
            </xs:element>
<xs:element name="root">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="meta">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="count" type="xs:positiveInteger"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="data">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element maxOccurs="unbounded" ref="row"/>
                        <!-- <xs:element maxOccurs="root/meta/count" ref="row"/> -->
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
        <xs:assert test="count(data/row) = meta/count"/>
        <xs:assert test="max(data/row/@id) = meta/count"/>
        <xs:assert test="every $x in (1 to meta/count) satisfies data/row[$x]/@id = $x"
                       xerces:message="Rule #2.1: attribute id shall start at 1 and go up till @total value."/>
    </xs:complexType>
    </xs:element>
</xs:schema>


Solution

  • Please try the following solution via XSD 1.1 and Xerces.

    Will minor adjustment it will work with Saxon, and other XSD 1.1 conformant processors.

    Even in Python: Is it possible to validate an XML file against XSD 1.1 in Python?

    It is using an assert with a quantified expression to express the desired logic.

    One single assert is covering all required logic:

    XML

    <root total="5">
        <r id="1"/>
        <r id="2"/>
        <r id="3"/>
        <r id="4"/>
        <r id="5"/>
    </root>
    

    XSD 1.1 #1

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
               xmlns:xs="http://www.w3.org/2001/XMLSchema"
               xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
               xmlns:xerces="http://xerces.apache.org" vc:minVersion="1.1">
        <xs:element name="root">
            <xs:complexType>
                <xs:sequence>
                    <xs:element maxOccurs="unbounded" ref="r"/>
                </xs:sequence>
                <xs:attribute name="total" use="required" type="xs:positiveInteger"/>
                <xs:assert test="every $x in (1 to @total) satisfies r[$x]/@id = $x"
                           xerces:message="Rule #2.1: attribute id shall start at 1 and go up till @total value."/>
            </xs:complexType>
        </xs:element>
        <xs:element name="r">
            <xs:complexType>
                <xs:attribute name="id" use="required" type="xs:positiveInteger"/>
            </xs:complexType>
        </xs:element>
    </xs:schema>
    

    XSD 1.1 #2

    Even better and more concise via deep-equal(). No need in any other asserts.

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
               xmlns:xs="http://www.w3.org/2001/XMLSchema"
               xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
               xmlns:xerces="http://xerces.apache.org" vc:minVersion="1.1">
        <xs:element name="root">
            <xs:complexType>
                <xs:sequence>
                    <xs:element maxOccurs="unbounded" ref="r"/>
                </xs:sequence>
                <xs:attribute name="total" use="required" type="xs:positiveInteger"/>
                <xs:assert test="deep-equal((r/xs:int(@id)),(1 to @total))"
                           xerces:message="Rule #2.1: attribute id shall start at 1 and go up till @total value."/>
            </xs:complexType>
        </xs:element>
        <xs:element name="r">
            <xs:complexType>
                <xs:attribute name="id" use="required" type="xs:positiveInteger"/>
            </xs:complexType>
        </xs:element>
    </xs:schema>