ZVON > Tutorials > XML Schema and Relax NG Tutorial
Index | >> Example 6 / 6 << | Prev | Next |
Contents > Unique and key > Keyref

Keyref

  1. XML Schema - keyref
  2. Relax NG + Schematron
XML Schema keys: key, keyref

1. XML Schema - keyref

The "keyref" lets you specify, that attribute/element refers to some node (which must be defined as "key" or "unique"). Now, the "key" element says: the elements "a" under "root" element must have unique value of "id" attribute and this attribute must be present. The "keyref" element specifies that element "b" refers to element "a" (via the attributes: b/@idref -> a/@id).

Valid document


<root xsi:noNamespaceSchemaLocation="correct_0.xsd" xmlns="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
  <AAA>
    <a id="x"/>
    <a id="y"/>
  </AAA>
  <BBB>
    <b idref="x"/>
    <b idref="y"/>
    <b idref="y"/>
  </BBB>
</root>

Invalid document
The document is not valid, because we reference non-existent id.


<root xsi:noNamespaceSchemaLocation="correct_0.xsd" xmlns="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
  <AAA>
    <a id="x"/>
    <a id="y"/>
  </AAA>
  <BBB>
    <b idref="x"/>
    <b idref="y"/>
    <b idref="z"/>
  </BBB>
</root>

Correct XML Schema (correct_0.xsd)
The "xpath" attribute of "selector" and "field" elements is a restricted XPath relative to the element in which it is declared (here it is relative to the "AAA" and "BBB" elements for "key" and "keyref" respectively). Note: the "key", "keyref", "unique" elements must come after (not before) "complexType". (See topLevelElement definition).


<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" >

  <xsd:element name="root">
    <xsd:complexType>
      <xsd:sequence minOccurs="1" maxOccurs="1">
        <xsd:element name="AAA" type="myAAA"/>
        <xsd:element name="BBB" type="myBBB"/>
      </xsd:sequence>
    </xsd:complexType>
    <xsd:key name="myId">
      <xsd:selector xpath="./AAA/a"/>
      <xsd:field xpath="@id"/>
    </xsd:key>
    <xsd:keyref name="myIdref" refer="myId">
      <xsd:selector xpath="./BBB/b"/>
      <xsd:field xpath="@idref"/>
    </xsd:keyref>
  </xsd:element>

  <xsd:complexType name="myAAA">
    <xsd:sequence minOccurs="1">
      <xsd:element name="a" minOccurs="1" maxOccurs="unbounded">
        <xsd:complexType>
          <xsd:attribute name="id" type="xsd:NCName" use="required"/>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>

  <xsd:complexType name="myBBB">
    <xsd:sequence minOccurs="1">
      <xsd:element name="b" minOccurs="1" maxOccurs="unbounded">
        <xsd:complexType>
          <xsd:attribute name="idref" type="xsd:NCName" use="required"/>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

2. Relax NG + Schematron

There is no such mechanism in Relax NG, but we can combine Relax NG and Schematron schemas.

Valid document


<root xmlns="">
  <AAA>
    <a id="x"/>
    <a id="y"/>
  </AAA>
  <BBB>
    <b idref="x"/>
    <b idref="y"/>
    <b idref="y"/>
  </BBB>
</root>

Invalid document
The document is not valid, because we reference non-existent id.


<root xmlns="">
  <AAA>
    <a id="x"/>
    <a id="y"/>
  </AAA>
  <BBB>
    <b idref="x"/>
    <b idref="y"/>
    <b idref="z"/>
  </BBB>
</root>

Correct Relax NG schema (correctRelax_0.rng)
This Relax NG will check tree structure, and verify the datatypes.


<grammar datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes" ns="" xmlns="http://relaxng.org/ns/structure/1.0" >

  <start>
    <element name="root">
      <ref name="myAAA"/>
      <ref name="myBBB"/>
    </element>
  </start>

  <define name="myAAA">
    <element name="AAA">
      <oneOrMore>
        <element name="a">
          <attribute name="id">
            <data type="NCName"/>
          </attribute>
          <empty/>
        </element>
      </oneOrMore>
    </element>
  </define>

  <define name="myBBB">
    <element name="BBB">
      <oneOrMore>
        <element name="b">
          <attribute name="idref">
            <data type="NCName"/>
          </attribute>
          <empty/>
        </element>
      </oneOrMore>
    </element>
  </define>
</grammar>

Correct Schematron schema
The Schematron rules below will check the uniqueness of the "a/@id" attribute and that each "b/@idref" attribute references existing "a/@id". Warning - not all Schematron implementations might support function "current()" (it's XSLT, not XPath function).


<schema xmlns="http://www.ascc.net/xml/schematron" >
  <pattern name="Element 'a' must have unique 'id' attribute">
    <rule context="a/@id">
      <assert test="count(//@id[. = current()]) = 1"> Id not unique! </assert>
    </rule>
  </pattern>
  <pattern name="'idref' must reference existing 'id'">
    <rule context="b/@idref">
      <assert test="/root/AAA/a/@id[. = current()]"> Id '
        <value-of select="."/>' does not exist!
      </assert>
    </rule>
  </pattern>
</schema>