Search code examples
xmlxsdxsd-validation

Ensure that the content of one element is equal to a key (keyref for content)


I have two lists of different elements (ruleRef and rule) in the same XML file. I'd like to make sure that the content of ruleRef equals the name of any rule.

In my XSD I already have a xs:key restriction like such:

<xs:key name="ruleName">
  <xs:selector xpath="rules/rule"/>
  <xs:field xpath="@name"/>
</xs:key>

I've also used this key before to validate the attribute of another element like such:

<xs:keyref name="differentRuleRef" refer="ruleName">
  <xs:selector xpath="rules/rule/differentRuleRef"/>
  <xs:field xpath="@referencedRule"/>
</xs:keyref>

Now I'd like to validate the actual content of an element instead of an attribute. E.g. in the following XML the second <ruleRef> element should be invalid:

<root>
  <rules>
    <rule name="rule1"/>
    <rule name="rule2"/>
  </rules>
  <ruleRefs>
    <ruleRef>rule1</ruleRef>
    <ruleRef>nonExistantRule</ruleRef> <!-- invalid -->
  </ruleRefs>
</root>

I've tried text() and data(.) before, but both of them are not valid in the XPath-subset. I've also tried . and ruleRef, but they didn't seem to validate anything.


Solution

  • Using . for the xs:field in your xs:keyref should be working, I rather suspect a mistatch in the XPath for the xs:selector.

    On the <root> element, defining the following in the schema is working on the XML sample you provided:

    <xs:element name="root" type="root">
        <xs:key name="ruleName">
            <xs:selector xpath="rules/rule"/>
            <xs:field xpath="@name"/>
        </xs:key>
        <xs:keyref refer="ruleName" name="differentRuleRef">
            <xs:selector xpath="ruleRefs/ruleRef"/>
            <xs:field xpath="."/>
        </xs:keyref>
    </xs:element>
    
    <xs:complexType name="root">
       <!-- define root content type here ...-->
    </xs:complexType>
    

    Another approach (somewhat simpler) could be to use the ID / IDREF mechanism, and you can get rid of the xs:key / xs:keyref definition in your shema, and modify the definition of rules and ruleRefs :

       <xs:element name="rule" maxOccurs="unbounded">
          <xs:complexType>
            <xs:attribute name="name" type="xs:ID" />
          </xs:complexType>
       </xs:element>
    
       <xs:element name="ruleRef" type="xs:IDREF" maxOccurs="unbounded">
       </xs:element>
    

    This will constraint the rule names to be unique.