xmlxsdassertxsd-validation

XSD: assert to check that values of several elements from same type are not equal


Using XML version 1.1

For a game I have build a complex structure called "connected_condition". Its code is looking somewhat like this:

    <xs:complexType name="connected_condition" mixed="true">
        <xs:sequence>
            <xs:choice minOccurs="0" maxOccurs="3">
                <xs:element name="instruction" type="command" minOccurs="0" maxOccurs="2"/>
                <xs:element name="condition" type="extendedConditionFunction" minOccurs="0" maxOccurs="1"/>
                <xs:element name="annotation" type="defined_annotation" minOccurs="0" maxOccurs="2"/>
            </xs:choice>
        </xs:sequence>
        <xs:attribute name="annotation" type="annotation"/>
        <xs:attribute name="clothes" type="clothesType"/>
        <xs:attribute name="extended">
            <xs:simpleType>
                <xs:union memberTypes="extendedParameter extendedFunction"/>
            </xs:simpleType>            
        </xs:attribute>
        <xs:attribute name="value" type="valueString"/>
        <xs:attribute name="lasting" type="children" use='required'/>
    </xs:complexType>

This structure is supposed to call one object of several possible attributes defined in my xsd-file for xml. The first attribute or element is supposed to be the condition (which can be of any of the predefined types) that is later to be checked. The second element and its attribute is always supposed to be the consequence that is triggered when the first attribute or element is fullfilled. A oversimplified example would look like this:

<connected_condition lasting ='false' annotation='stand'><annotation extended='roll_twice'/></connected_condition>

In this example, when a player is in the state of standing then he is also supposed to roll a dice twice instead of only once. The attribute lasting furthermore defines, if this is a one-time use or if this effect will be enduring until it is canceled out. Since this structure is supposed to check and inflict several different effects there can also be used similar elements. So a structure like this would be valid as well:

<connected_condition lasting='false' annotation='stand'><annotation annotation='sit'/></connected_condition>

As you can imagine, this bears the possibility that both elements there could actually have the same attribute and like that also an equal value. So there could be a structure like: <annotation='stand'><annotation annotation='stand'/>. To prevent this I use an assert in my xsd-file which looks like this:

<xs:assert test="not (. eq (annotation/@annotation))"/>

So far this works flawless. However, my structure is becoming more flexible and is also supposed to call functions that will give new conditions (for example in order to delete an earlier state or the condition itself when another condition is fullfilled).

This leads me to the issue at hand. My assert is working when I use only once childe element of similar type. But it always fails when I use more than one child element. The structure I would want to have valid would looks something like this for example:

<connected_condition lasting='true' extended='sit'><annotation annotation='roll_twice'/><annotation annotation='changeMood'/><annotation annotation='delete'/></connected_condition>

Here the player is again supposed to roll the dice twice, this time when sitting. This effect is supposed to last until it is canceled out. However it is supposed to be deleted as soon as the mood of the game changes which can be triggered by another effect or condition. The problem is that at the moment these structures fail the assertion-check on default as soon as they have more than one element annotation of the same type. How do I edit my assert that it checks, if in such more complex conditional structures with several child elements of the same type the values of these elements are never the same without failing them on default?


Solution

  • I'm not sure I have fully understood your requirements, but...

    You can generally do this with a uniqueness constraint. For example, in the element declaration for connected_condition (not the type declaration) use <xs:unique selector="annotation" field="@annotation"/>.

    If you can't do that (for example because the elements that need to be unique require a more complex predicate) then you can use an assertion of the form test="count(annotation/@annotation) = count(distinct-values(annotation/@annotation))".