Search code examples
xmlxsdintegerattributesassert

XSD: How to make the integer range of a value dependent on other element?


This is the second time I ask this question: In an XML document (using Version 1.1) an element random has two integers: An integer start that normally only should have a range of value from min=1 and max=9. And an integer end that is always supposed to be bigger then start with a max value of max=10. Being defined like this:

    <xs:complexType name="random">
        <xs:attribute name="start">
            <xs:simpleType>
                <xs:restriction base="xs:integer">
                    <xs:minInclusive value="1"/>
                    <xs:maxInclusive value="9"/>
                </xs:restriction>
            </xs:simpleType>
        </xs:attribute>
        <xs:attribute name="end">
            <xs:simpleType>
                <xs:restriction base="xs:integer">
                    <xs:minInclusive value="2"/>
                    <xs:maxInclusive value="10"/>
                </xs:restriction>
            </xs:simpleType>
        </xs:attribute>
        <xs:assert test="@start le @end"/>
    </xs:complexType>

But now in some rare cases when there is the attribute bigValue in another element present, I want these integers to actually be able to have a much higher value. To achieve that I can have the element random as a child-element of the complexType-item annotation which is defined in the XSD-file like this:

    <xs:complexType name="annotation" mixed="true">
        <xs:sequence>
            <xs:element name="random" type="random" minOccurs="0" maxOccurs="1"/>
        </xs:sequence>
        <xs:attribute name="annotation">
            <xs:simpleType>
                <xs:union memberTypes="comments dice"/>
            </xs:simpleType>
        </xs:attribute>
 <---- INSERT ASSERT HERE ---->
    </xs:complexType>

For example in a case where bigValue is defined, start is supposed to have a value of 20 and end of 50:

<annotation extended='bigValue'/><random start='20' end='50'/>

Is there any way how to do that without having to change the value range of start and end (because in almost all other cases it is not supposed to be bigger than 9 or respectively 10). Can this be achieved with an assert for example? Or is this not possible at all and the better approach is to limit the max values by an assert if bigValue is absent? I already tried plenty of assertions like the following ones but all of them fail to compile, only throwing the "XPST0003 - Assertion XPath expression couldn't compile successfully"-error. Here is what I tried:

<xs:assert test="if exists(annotation/bigValue) or every $x in .//@random.start satisfies $x le 10"/>
<xs:assert test="exists(annotation/bigValue) or every $x in .//@random.start satisfies $x le 10"/>
<xs:assert test="if ((random) and (annotation) and exists(annotation/bigValue)) or every $x in .//@random.start satisfies $x le 10"/>

Additionally I tried a restructured assert for the case that the value of start and end are defined higher to begin with. But even though I found an assert that compiled, it broke the XML-document completely telling me on any element that the assertion evaluation didn not succeed so I had to delete it as well. The assert I used was:

<xs:assert test="if (random.start le 9) then (annotation.annotation = 'bigValue') else not(random.start le 9)"/>

So at this point I'm fairly clueless and confused how to do it. Can you please help me?


Solution

  • Your first and third xs:assert attempts are clearly nonsense: an XPath if expression needs parentheses around the condition and then and else clauses. The fact that you attempt this and have no idea why it's wrong casts serious doubt on how you are approaching the task - you seem to be trying to write code by trial and error, without using any reference material to guide you.

    The second attempt is closer, but if you enter this as an XPath expression into the XPath box in Oxygen, it tells you what is wrong:

    exists(annotation/bigValue) or every $x in .//@random.start satisfies $x le 10
    

    Unexpected token 'every' at start of expression

    It needs to be

    exists(annotation/bigValue) or (every $x in .//@random.start satisfies $x le 10)
    

    which is syntactically valid. However, I suspect you don't have an attribute called @random.start, instead I suspect you were trying to refer to the @start attribute of the random element, which would be .//random/@start.