How to check that attributes are unique with RelaxNG?

732 views Asked by At

With RelaxNG, can I check whether or not the value of an attribute is unique within an enclosing element?

For example, this castle should validate:

<castle>
  <room>
    <door to="North" />
    <door to="South" />
  </room>
  <room>
    <door to="North" />
  </room>
</castle>

But this should not (duplicate door in same room):

<castle>
  <room>
    <door to="Dungeon" />
    <door to="Dungeon" />
  </room>
</castle>

I'm using RelaxNG (compact). I don't know the attribute values 'ahead of time', only that they should be unique within a room.

Thanks!

1

There are 1 answers

1
Rintze Zelle On BEST ANSWER

To my knowledge this can't be done in pure RELAX NG. You could use (embedded) Schematron, as we did for the Citation Style Language schema. If you do take this route, note that not all RELAX NG validators parse embedded Schematron, and that support for standalone Schematron schemas is also limited. E.g. the popular Jing XML validator only supports the older Schematron 1.5 version, not the newer ISO Schematron.

For our project, where we use Jing, we use a script to first convert our RELAX NG Compact schema to the RELAX NG XML format (with Trang), then extract the Schematron rules from the RELAX NG XML schema into a standalone Schematron schema (with Saxon and the RNG2Schtrn.xsl XSLT style sheet), and finally validate against the extracted Schematron schema with Jing.

If this hasn't scared you off, I cobbled together the following Schematron 1.5 schema for your problem:

<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://www.ascc.net/xml/schematron">
  <sch:pattern name="duplicateAttributeValues">
    <sch:rule context="//room/door[@to]">
      <sch:report test="preceding-sibling::door/@to = @to">Warning: @to values should be unique for a given room.</sch:report>
    </sch:rule>
  </sch:pattern>
</sch:schema>

When run on the following XML document,

<?xml version="1.0" encoding="utf-8"?>
<castle>
  <room>
    <door to="North"/>
    <door to="South"/>
    <door to="West"/>
  </room>
  <room>
    <door to="West"/>
    <door to="North"/>
    <door to="West"/>
  </room>
</castle>

Jing will report

Error: Warning: @to values should be unique for a given room.
From line 11, column 5; to line 11, column 21
th"/>↩ <door to="West"/>↩ </r