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>
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>