xmlxsdsaxonxsd-validationxsd-1.1

XSD 1.1 assert with XPath containing a predicate involving xsi:type fails


I have an XML document about books:

<books xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <book>
        <genreRef>ID000</genreRef>
    </book>
    <genres>
        <genre xsi:type="Mystery" referenceId="ID000"/>
        <genre xsi:type="Action"  referenceId="ID001"/>
        <genre xsi:type="Fantasy" referenceId="ID002"/>
    </genres>
</books>

You can see that the <genreRef> element points to a <genre> element of type ‘Mystery’. That’s what I want—I want to constrain the <genreRef> element to point exclusively to <genre> elements of type ‘Mystery’. Pointing to a <genre> element of type ‘Action’ or ‘Fantasy’ is an error, which I want my XSD 1.1 schema to detect.

Below is my XSD 1.1 schema. Notice the <assert> element on the root element. The XPath expression says the <genreRef> element in every <book> element points to a <genre> element with type ‘Mystery’. I confirmed that the XPath expression is correct by evaluating it in Oxygen. However, when I validate the above XML instance document against the below XSD, the SAXON schema validator reports “Element books does not satisfy assertion every $i in book satisfies some $j in genres/genre[@xsi:type = 'Mystery'] satisfies $i/genreRef eq $j/@referenceId

What am I doing wrong? How can I fix this?

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    
    <xs:element name="books">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="book" maxOccurs="unbounded">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="genreRef" type="xs:IDREF"/>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
                <xs:element name="genres">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="genre" type="Genre" maxOccurs="unbounded"/>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
            <xs:assert test="every $i in book satisfies some $j in genres/genre[@xsi:type = 'Mystery'] satisfies $i/genreRef eq $j/@referenceId" />
        </xs:complexType>
    </xs:element>
    
    <xs:complexType name="Genre" abstract="true">
        <xs:sequence>
        </xs:sequence>
        <xs:attribute name="referenceId" use="required" type="xs:ID"/>
    </xs:complexType>
    
    <xs:complexType name="Mystery">
        <xs:complexContent>
            <xs:extension base="Genre"/>
        </xs:complexContent>
    </xs:complexType>
    
    <xs:complexType name="Action">
        <xs:complexContent>
            <xs:extension base="Genre"/>
        </xs:complexContent>
    </xs:complexType>
    
    <xs:complexType name="Fantasy">
        <xs:complexContent>
            <xs:extension base="Genre"/>
        </xs:complexContent>
    </xs:complexType>

</xs:schema>

Solution

  • I think it is a type error, @xsi:type returns a QName, you can't compare a string to a QName, thus if you change your assert to <xs:assert test="every $i in book satisfies some $j in genres/genre[@xsi:type = QName('', 'Mystery')] satisfies $i/genreRef eq $j/@referenceId" /> you get what you want.