Search code examples
xmlxsdschemaschematron

Is there a way to create a rule to check the value of an attribute when another attribute is present?


I am currently working on a simple schema with schematron rules embedded. One of the rules is to check the value of an attribute on a specific element called @handle which should start with 12345 for example. But if that same element has another optional attribute called @remark this rule doesn't apply as the value would be random.

I have the following XML:

<record handle="12345/random numbers"/>
<record handle="abcdef" remark="value"/>

And the following schema snippet:

<xs:element name="record">
        <xs:annotation>
            <xs:appinfo>
                <sch:pattern id="handle check"
                    xmlns:sch="http://purl.oclc.org/dsdl/schematron">
                    <sch:rule context="@handle and not(../@remark)">
                        <sch:assert test="starts-with(.,'12345')">Handle-id, should start with 12345</sch:assert>
                    </sch:rule>
                </sch:pattern>
            </xs:appinfo>
        </xs:annotation>
        <xs:complexType>
            <xs:sequence>
                <xs:element name="title" minOccurs="0" maxOccurs="unbounded"/>
            </xs:sequence>
            <xs:attribute name="material" use="optional"/>
            <xs:attribute name="remark" type="coll:remark" use="optional"/>
            <xs:attribute name="handle" use="required">
            </xs:attribute>
        </xs:complexType>
    </xs:element>

But using this I get stylesheet compilation errors. If I delete the part and not(../@remark), it works fine and creates errors at the elements with @remark like expected, but I cannot seem to exclude this and am wondering if this is even possible.


Solution

  • The @context of a rule must refer to a node. @handle is a node, and @remark is also a node, but @handle and not(../@remark) is an expression that evaluates to true or false. It's not a node.

    You could rewrite the rule/@context to be:

    @handle[not(../@remark)]

    ... which would fire on all @handle attribute nodes that do not have a @remark sibling attribute.