This work is licensed under a Creative Commons |
While XML Schema is the most popular schema language in use today and for the foreseeable future, it is only one representative from a class of languages which are all designed for the purpose of testing whether some XML document satisfies a set of constraints. This test could of course also be conducted programmatically, but this is not portable and not easily maintainable. Schema languages thus often use a declarative approach to specifying how to conduct validation. A very simple yet very powerful language for this is Schematron, which uses the expressive power of XPath for testing whether a document satisfies a set of conditions. Schematron is rule-based in contrast to the more traditional grammar-based schema languages and complements these very well.
&
is supported (all is extremely limited)chess = white, (black, white)*, black?
content model
<!ELEMENT document (heading, chapter) > <!ELEMENT heading (#PCDATA) > <!ELEMENT chapter (heading, para+) > <!ATTLIST chapter id ID #REQUIRED > <!ELEMENT para (#PCDATA) >
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="document"> <xs:complexType> <xs:sequence> <xs:element ref="heading"/> <xs:element ref="chapter"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="heading" type="xs:string"/> <xs:element name="chapter"> <xs:complexType> <xs:sequence> <xs:element ref="heading"/> <xs:element name="para" type="xs:string" maxOccurs="unbounded"/> </xs:sequence> <xs:attribute name="id" type="xs:ID"/> </xs:complexType> </xs:element> </xs:schema>
start = document document = element document { heading, chapter } heading = element heading { text } chapter = element chapter { attribute id { text }, heading, element para { text }+ }
validationbefore processing
<schema xmlns="http://www.ascc.net/xml/schematron"> <title>Address Checking</title> <pattern name="Phone Number Checking"> <rule context="address"> <assert test="(count(phone[@type = 'voice']) > 0) and (count(phone[@type = 'fax']) > 0)">there must be at least one voice and one fax number</assert> </rule> </pattern> </schema>
executableand
outputXSLT elements
<xsl:template match="rule"> <xsl:template match="{@context}"> <xsl:apply-templates select="assert"/> </xsl:template> </xsl:template>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias" xmlns:sch="http://www.ascc.net/xml/schematron" >
<xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>
<!-- ASSERT and REPORT --> <xsl:template match="sch:assert | assert"> <xsl:if test="not(@test)"> <xsl:message>Markup Error: no test attribute in <assert></xsl:message> </xsl:if> <axsl:choose> <axsl:when test="{@test}"/> <axsl:otherwise> <xsl:call-template name="process-assert"> <xsl:with-param name="role" select="@role"/> <xsl:with-param name="id" select="@id"/> <xsl:with-param name="test" select="normalize-space(@test)" /> <xsl:with-param name="icon" select="@icon"/> <xsl:with-param name="subject" select="@subject"/> <xsl:with-param name="diagnostics" select="@diagnostics"/> </xsl:call-template> </axsl:otherwise> </axsl:choose> </xsl:template> <xsl:template match="sch:report | report"> <xsl:if test="not(@test)"> <xsl:message>Markup Error: no test attribute in <report></xsl:message> </xsl:if> <axsl:if test="{@test}"> <xsl:call-template name="process-report"> <xsl:with-param name="role" select="@role"/> <xsl:with-param name="test" select="normalize-space(@test)" /> <xsl:with-param name="icon" select="@icon"/> <xsl:with-param name="id" select="@id"/> <xsl:with-param name="subject" select="@subject"/> <xsl:with-param name="diagnostics" select="@diagnostics"/> </xsl:call-template> </axsl:if> </xsl:template>
assert
assert
is used to specify assertionsfalse
, the assertion's content is output<!ELEMENT ENTRY (NAME, ADDRESS, PHONENUM+, EMAIL) >
( count(NAME) = 1 and count(ADDRESS) = 1 and count(EMAIL) = 1 ) and ( NAME[following-sibling::ADDRESS] and ADDRESS[following-sibling::PHONENUM] and PHONENUM[following-sibling::EMAIL] ) and ( count(NAME|ADDRESS|PHONENUM|EMAIL) = count(*) )
report
report
is used to generate reportstrue
, then the assertion's content is outputassert
and report
are inverseassert
is used to test conformance (it outputs errors)report
id used to report observations (it outputs messages)