Search code examples
xmlxsd

How can I validate one element based off of another element in XSD?


I have this XML:

    <record>
      <element>
        <freeText>There is a big red cow.</freeText>
        <terms>
          <term>big</term>
          <term>red</term>
        <term>
      </element>
      <element>
        <freeText>Technically this is irrelevant but I wanted to figure out if this assertion conflicts somehow.</freeText>
        <terms>
          <term>farm</term>
          <term>animal</term>
        </terms>
       </element>
    </record>   

What I would like to do is create an assertion that if the freeText field contains "cow", then one of the terms must also be "cow".

The relevant XSD I have is this:

<xs:element name ="terms">
  <xs:complexType>
   <xs:sequence>
    <xs:element maxOccurs="unbounded" ref="term"/>
   </xs:sequence>
   <xs:assert test = "if (contains(../description,'cow') = true()) then (../terms/term = 'cow') else (term = *)"/>
   <xs:assert text = "if (term = 'farm') then (term = 'animal') else (term = *)"/>
  </xs:complexType>
</xs:element>

No matter how I write my xpath, I can't figure out how to get the first assertion to validate properly. The second assertion works fine, I have a bunch of terms with one-way co-occurrence constraints, and I've never had a problem with that. But with the assertion that checks freeText it doesn't seem to be looking in the right places. Would appreciate some help.


Solution

  • There's an absolute context rule that an assert can only look within the SUBTREE of the element on which it is defined. That's why you cannot use navigation up, i.e. contains(../description,'cow')

    Please try the following solution.

    Input XML

    <record>
        <element>
            <freeText>There is a big red cow.</freeText>
            <terms>
                <term>big</term>
                <term>small</term>
            </terms>
        </element>
        <element>
            <freeText>Technically this is irrelevant but I wanted to figure out if this assertion conflicts somehow.</freeText>
            <terms>
                <term>farm</term>
                <term>animal</term>
            </terms>
        </element>
    </record>
    

    XSD 1.1

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
      <xs:element name="record">
        <xs:complexType>
          <xs:sequence>
            <xs:element maxOccurs="unbounded" ref="element"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="element">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="freeText"/>
            <xs:element ref="terms"/>
          </xs:sequence>
          <xs:assert test = "if (not(contains(freeText,'cow'))) then true() else 
            terms/term[. = 'cow']"/>
        </xs:complexType>
      </xs:element>
      <xs:element name="freeText" type="xs:string"/>
      <xs:element name="terms">
        <xs:complexType>
          <xs:sequence>
            <xs:element maxOccurs="unbounded" ref="term"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="term" type="xs:NCName"/>
    </xs:schema>