3.4. Schematron

In the previous sections, we have seen two different ways of validating the content of an XML file: while DTDs can only validate the element nesting structure, XML and RELAX NG schemas can also validate the content of the elements by enforcing local constraints such as the length of strings, regular expressions or range on the allowed values. These types of constraints, defined by means of grammars described the well-formedness of structure and values, can be characterized as syntactic. But in typical applications, more semantic types of constraints could be appropriate for checking long distance dependencies that would be awkward or even impossible to express by means of grammar rules or regular expressions, e.g. a starting date in an attribute or element should be earlier than an ending date in another one. In this case, a rule-based approach is more appropriate.

Typical global semantic constraints can be defined to take into account relations between elements and attributes: for example, an attribute with a given value could imply the definition of another element. It could be necessary that a certain value is equal to the sum of other values. In certain cases, it would even be interesting to enforce constraints between different XML files. These checks are outside the scope of the XML validation methods we have presented in the previous sections, but an alternative approach, called Schematron [20], [86] has been developed to cope with this type of validation. Instead being grammar-based, Schematron is a rule-based validation approach to define assertions in which XPath expressions are evaluated. XPath expressions will be described more fully in Chapter 4, but for the moment they can understood as defining values computed over different parts of an XML file; in fact, Schematron even allows combining values spanning more than one XML file. In principle, Schematron could be used for also defining local constraints, but as they are much more easily defined by grammars, we will use Schematron patterns as supplementary validation rules used in conjunction with an XML Schema. The Schematron specification describes how patterns can be embedded within other XML schema notations such as XML Schema, RELAX NG and RELAX NG Compact.

Similarly to an XML Schema and RELAX NG file, a Schematron file is a well-formed (and valid) XML file that defines validation in terms of pattern elements containing rule elements. A rule defines a context for internal XPath expressions used in assertion and report elements. When the XPath expression of an assertion (resp. report) is false (resp. true), then the content of the element is output as validation message. The validation message can contain variable parts to customize the message according to the context and the specific elements involved.

Typical semantic validation uses are the following:

cooccurrence checking
defines a relationship between attributes, elements or a mix of these not only within a single XML document but also across multiple documents; it can also place constraints on the context of mixed content which cannot be enforced with a grammar-based approach.
cardinality checking
defines a constraint on the number of occurrences of data not only within a single element but even over a portion of a document.
algorithmic checking
involving some computation on values occurring in the document

Example 3.8 shows Schematron rules for the cellar book. These rules should be combined with the XML Schema or RELAX NG in order to validate the instance file both syntactically and semantically. We use the ISO Schematron [67] that has been recently standardised,. It differs in some respects from the Schematron 1.6 dialect [87], but the main ideas remain valid in both formalisms.

Table 3.4 presents the XML elements we use in our example to define the types needed for the validation of our wine catalog and cellar book.

Table 3.4. Schematron syntax reminder

<schema targetNameSpace="URI">
    title? ns? pattern+ 
</schema>
<ns prefix="QName"
 uri="URI"/>
<title>
    PCDATA
</title>
<pattern abstract="yes | no"
 id="ID"
 is-a="IDREF">
    param* rule+
</pattern>
<param name="QName"
 value="XPathExpr"/>
<rule context="XPathExpr">
    {let | report | assert}*
</rule>
<let name="QName"
 value="XPathExpr"/>
<report test="XPathExpr">
    {PCDATA | value-of | name}*
</report>
<assert test="XPathExpr">
    {PCDATA |value-of | name}*
</assert>
<value-of select="XPathExpr"/>
<name/>

A reminder of the subset of the Schematron syntax used in Example 3.8. Names in italics refer to other elements. PCDATA refers to XML string content . Regular expressions (see Table B.1) are used to describe the allowed forms.


Example 3.8. [CellarBook.sch] ISO-Schematron for the cellar book to validate Example 2.2.

This file can be used in combination with the XML Schema shown in Example 3.3.

  1 <?xml version="1.0" encoding="UTF-8"?>
    <schema xmlns="http://purl.oclc.org/dsdl/schematron"                 (1)
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"                 (2)
        queryBinding="xslt2">
  5     <title>Validation using Schematron rules</title>                 (3)
        <ns prefix="cat"                                                 (4)
            uri="http://www.iro.umontreal.ca/lapalme/wine-catalog"/>
        <xsl:key name="colors" match="/cellar-book/cat:wine-catalog/cat:wine" (5)
            use="cat:properties/cat:color"/>
 10     
        <pattern>                                                        (6)
            <rule context="wine">
                <report test="rating/@stars>1 and not(comment)"> 
                    There should be a comment for a wine with more than one star.
 15             </report>
            </rule>
        </pattern>
        
        <pattern>                                                        (7)
 20         <rule context="cellar">
                <let name="nbBottles" value="sum(wine/quantity)"/>       (8)
                <report test="$nbBottles &lt; 10">
                    Only <value-of select="$nbBottles"/> bottles left in the cellar.
                </report>
 25             <!-- nb of bottles of each color in the cellar -->
                <let name="winesFromCellar" value="/cellar-book/cellar/wine"/>(9)
                <let name="nbReds" 
                 value="sum($winesFromCellar[@code=key('colors','red')/@code]/quantity)"/>
                <let name="nbWhites" 
 30              value="sum($winesFromCellar[@code=key('colors','white')/@code]/quantity)"/>
                <let name="nbRosés" 
                 value="sum($winesFromCellar[@code=key('colors','rosé')/@code]/quantity)"/>
                <let name="nbColors" value="$nbReds+$nbWhites+$nbRosés"/>
                <!-- check for a well balanced cellar!!! -->
 35             <assert test="$nbReds>$nbColors div 3">
                    Not enough reds (<value-of select="$nbReds"/> over 
                    <value-of select="$nbColors"/>) left in the cellar.
                </assert>
                <assert test="$nbWhites>$nbColors div 4">
 40                 Not enough whites (<value-of select="$nbWhites"/> over 
                    <value-of select="$nbColors"/>) left in the cellar.
                </assert>
                <assert test="$nbRosés>$nbColors div 4">
                    Not enough rosés (<value-of select="$nbRosés"/> over 
 45                 <value-of select="$nbColors"/>) left in the cellar.
                </assert>
                <!-- check for consistency within number of bottles -->
                <assert test="$nbBottles=$nbColors">                     (10)
                    Inconsistent count of bottles: total is <value-of select="$nbBottles"/> 
 50                 but the count by colors is <value-of select="$nbColors"/>: 
                    (<value-of select="$nbReds"/> reds, <value-of select="$nbWhites"/> 
                    whites and <value-of select="$nbRosés"/> rosés).
                </assert>
            </rule>
 55     </pattern>
        
        <pattern abstract="true" id="spacesAtStartEnd">                  (11)
            <rule context="comment|cat:comment|cat:food-pairing|cat:tasting-note">
                <report test="starts-with($elem,' ') or 
 60                           substring($elem,string-length($elem))=' '">
                    A <value-of select="name($elem)"/> element within a <name/> 
                    should not start or end with a space.
                </report>
            </rule>
 65     </pattern>
        <pattern is-a="spacesAtStartEnd">                                (12)
            <param name="elem" value="cat:bold"/>
        </pattern>
        <pattern is-a="spacesAtStartEnd">                                (13)
 70         <param name="elem" value="cat:emph"/>
        </pattern>
        
    </schema>

1

ISO-Schematron rules are defined within an XML element defined in a specific namespace. We also declare the xsl namespace because it will be needed for using xsl:key line 8‑5 for indexing

2

By setting queryBinding attribute to xslt2, it is possible to use XPath 2.0 expressions in the assertions. By default, it is limited to XPath 1.0.

3

Gives an informative title to a set of validation rules.

4

Defines the namespace prefix to be used within XPath expressions that refer to elements of the wine catalog (see line 8‑5). For implementation reasons, it is not enough, or even necessary, to define the namespace with a xmlns attribute.

5

Declares that wine elements of the catalog are to be indexed by their color, used in line 26‑9.

6

Defines a cooccurrence constraint within a wine element: when the rating is more than one star then there should be a comment element. The rule element gives the context and the content of the report element defines the message which is output when the XPath expression of the test attribute evaluates to true.

7

A pattern that defines a rule implementing a mix of cooccurrence and algorithmic constraint within a cellar element.

8

There should be a warning when the total number of bottles in the cellar is less than 10. This is obtained by summing the values of all the quantity elements of the cellar. We save the total in the local variable nbBottles and use its value in the test by prefixing its name by $. The message can be customized by embedding value-of elements which are replaced by the result of the evaluation of the select attribute.

9

Defines a mix of cooccurrence and algorithmic constraints by computing the total number of bottles of each color and asserting that there should be at least a third of the number of bottles that are red, one fourth of both rosés and whites. The codes of wines of a given color are found by searching with the key XPath function. An assert element differs from a report in that the message is output when the test expression is false. We define a list of local variables which can be used in many assertions within the same context.

10

Checks that the total number of bottles in the cellar is equal to the sum of the numbers of each color. This an internal consistency check which in principle should never appear because it would mean that there a wines that are not red, white or rosé. But these values are the only ones allowed by the XML Schema

11

Defines an abstract pattern for generating two other patterns (line 66‑12, line 69‑13). This abstract pattern can be applied to $elem elements in many contexts; it makes sure that the string content of these elements do not start or end by a space. This is an example of the kind of validation possible on mixed content on which XML Schema place few constraints.

12

Instantiates the abstract pattern defined at line 57‑11 for the cat:bold elements.

13

Instantiates the abstract pattern defined at line 57‑11 for the cat:emph elements.


This example shows how new kinds of validation rules can be applied. The main advantages being the possibility of enforcing long-distance or algorithmic dependencies that cannot be implemented by the grammar-based validation offered by DTD and XML Schemas. Another advantage is the possibility of giving more meaningful error messages for the user than the ones generated automatically by a grammar based validation. As these validations are enforced by run-time checking once the user has entered the values in the file, it is not possible to determine a list of allowed choices that can be displayed by an XML editor at any point in the file. Although the ISO specification is relatively short (about 20 pages), we have not covered here all aspects of Schematron. In particular, we have not described the order of evaluation of the patterns and rules and we omitted some elements that are used for formatting and organizing processing phases and diagnostic messages, even multi-lingual ones.

In principle, Schematron could be used as the sole validation mechanism for XML instance files but simple sequencing constraints that are easy to express in a grammar must rely on complex XPath expressions involving ::following-sibling[1]. So in practice, it is more convenient to use both types of validation. But if having two separate validation files is not suitable, it is possible to embed Schematron rules within xs:appinfo and xs:annotation elements of an XML Schema. Similar embedding conventions have be defined for both RELAX NG and RELAX NG Compact.