Sometimes it is useful to have the help of keys (as in xsl:keys)
when tracking matches. My use came with this document. The html output
is generated by using the id values of each chapter. So a chapter
called keys.html
will be derived from the chapter
having xml:id
value of keys. That's
helpful and aids navigation. Except for the times when the author
stops thinking. In a chapter about schemas I named the chapter
schema, and also created a file, linked from that chapter, which also
happened to be named schema.html
This is where keys come in. The validation requirement is to check if any chapter has an attribute xml:id, which is the same as an xlink:href attribute on a link element, which also has a .html extension. Clear? No, nor to me. A small example, the input document, see Example 7.1, “File input.keys.xml, showing a chapter and an xlink with matching names ”
Example 7.1. File input.keys.xml, showing a chapter and an xlink with matching names
<?xml version="1.0" encoding="utf-8" ?> <doc xmlns:xlink="http://www.w3.org/1999/xlink"> <chapter xml:id="c1"> <title>chapter title</title> <para>Chapter content</para> </chapter> <chapter xml:id="c2"> <title>chapter title</title> <para>xx</para> <para>yy</para> <para>zz</para> </chapter> <chapter xml:id="c3"> <para>Invalid first child of chapter</para> <title>chapter title</title> <para>xx</para> <para>yy</para> <para>This paragraph uses a link to a file called <a xlink:href="c3.html">c3.html</a></para> </chapter> </doc>
The processing system (docbook) will generate a file
c3.html
from the chapter, which will probably
overwrite the other file with the same name, linked to from this
chapter! Schematron and keys to the rescue.
The Schematron file (Example 7.2, “The Schematron file for the keys example.”) has two new
features. Firstly another namespace declaration for prefix xlink, both
in the document element and in the ns
element. The xsl namespace is also added to the document
element. Next, a key is defined, outside of any patterns, as required
by annex C of the standard. Finally, the key is used in an assert statement.
Example 7.2. The Schematron file for the keys example.
<?xml version="1.0" encoding="iso-8859-1"?> <iso:schema xmlns="http://purl.oclc.org/dsdl/schematron" xmlns:iso="http://purl.oclc.org/dsdl/schematron" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" queryBinding='xslt2' schemaVersion="ISO19757-3"> <iso:title>Test ISO schematron file. Introduction mode </iso:title> <iso:ns prefix='xlink' uri="http://www.w3.org/1999/xlink"/> <!-- Define the key, anything in the xsl ns is copied through --> <xsl:key name="href" match="a" use="@xlink:href"/> <iso:pattern id="name.checks"> <iso:title>Check for id names and linked names which are the same, excluding extension</iso:title> <iso:rule context="chapter[@xml:id]"> <iso:assert test="not(key('href', concat(@xml:id, '.html')))">Warning. if chapter id value is used as html filename, there will be a conflict with <iso:value-of select="@xml:id"/>.html </iso:assert> </iso:rule> </iso:pattern> </iso:schema>
I hope that is clear. The principle is to check all links in the
document (the a
element), ensuring that the
xlink:href
value does not match the
current xml:id
value! When run (noting the error in the input file),
the output is as Example 7.3, “The resulting output”
Example 7.3. The resulting output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<svrl:schematron-output xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:svrl="http://purl.oclc.org/dsdl/svrl"
xmlns:sch="http://www.ascc.net/xml/schematron"
xmlns:iso="http://purl.oclc.org/dsdl/schematron"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
title="Test ISO schematron file. Introduction mode "
schemaVersion="ISO19757-3">
<svrl:ns-prefix-in-attribute-values uri="http://www.w3.org/1999/xlink" prefix="xlink"/>
<svrl:active-pattern name="Check for id names and linked names which are the same, excluding extension"
id="name.checks"/>
<svrl:fired-rule context="chapter[@xml:id]"/>
<svrl:fired-rule context="chapter[@xml:id]"/>
<svrl:fired-rule context="chapter[@xml:id]"/>
<svrl:failed-assert test="not(key('href', concat(@xml:id, '.html')))" location="/doc[1]/chapter[3]">
<svrl:text>Warning. if chapter id value is used as html filename, there
will be a conflict with c3.html
</svrl:text>
<svrl:text/>
</svrl:failed-assert>
</svrl:schematron-output>
Because of the power of keys, it is worth considering them for more complex cross referencing tasks
Be aware of where your keys are, and the restrictions regarding the use of keys with respect to one source file.
Of special note is the context in which the keys operate. The
match
attribute is with respect to the
main document, not the schematron file? The use
and name
attributes are used as in XSLT.
In the XSLT implementation mentioned here, keys are only allowed at the top level, just as in XSLT