Converting XML Schemas to Schematron: (#12) Some better diagnostics

This article first appeared in a blog on O'Reilly on January 30, 2008.

One simplification I have made in the XSLT code presented so far is that except for datatypes I have elided the issue of diagnostics. Yet the ability to provide better diagnostics is one of the value propositions for Schematron. So lets quickly add in some diagnostics!

In Schematron schemas, a distinction is made between assertions, which are positive natural language statements about what should be found in a schema (and if possible why!) and diagnostics which provide information about errors for users. So the schema might say “Element X should be followed by element Y” and the diagnostics might say “Element X was followed by element Z”. The user gets both pieces of information.

But in a well-written schema, the assertions can pretty much be printed off without theirXPath paraphernalia as bullet points and read as software requirements or human-usable documentation.

So here is our basic diagnostics section. These are each linked to from the appropriate assertions using the diagnostics attribute to reference the diagnostic element’s ID..

<sch:diagnostics>
                        <sch:diagnostic id="d1">This element was found:
                                "<sch:value-of select="*/name()"/>".</sch:diagnostic>

                        <sch:diagnostic id="typo-element">This element was found:
                                "<sch:name/>" in "<sch:value-of select="parent::*/name()"/>".</sch:diagnostic>

                        <sch:diagnostic id="typo-attribute">This attribute was found:
                                "<sch:name/>" on "<sch:value-of select="parent::*/name()"/>".</sch:diagnostic>

                        <sch:diagnostic id="expected-element">This element was found:
                                "<sch:name/>" in "<sch:value-of select="parent::*/name()"/>".</sch:diagnostic>

                        <sch:diagnostic id="expected-attribute">This attribute was found:
                                "<sch:name/>" on "<sch:value-of select="parent::*/name()"/>".</sch:diagnostic>

                        <sch:diagnostic id="unexpected-immediate-follower">This element was found:
                                "<sch:value-of select="following-sibling::*[1]/name()"/>".</sch:diagnostic>

                        <xsl:comment>Generating Diagnostics for xs:all/xs:elements 
                        <xsl:for-each select="xs:element[.//xs:all]//xs:all/xs:element">
                                <xsl:variable name="ancestor-element" select="ancestor::xs:element/@name"/>
                                <xsl:variable name="element-name" select="if (@name) then @name else @ref"/>
                                <sch:diagnostic id="{concat('d2-',$ancestor-element,'-',$element-name)}">
                                <sch:value-of select="count($element-name)"/>
                                        "<xsl:value-of select="$element-name"/>" elements were found</sch:diagnostic>
                        </xsl:for-each>

                        <!-- generate diagnostic for each standard datatypes -->
                        <xsl:call-template name="generate-standard-datatypes-diagnostics"/>
                </sch:diagnostics>

This is the last in this round of articles on the XSLT to Schematron converter about schema generation, probably. Thanks to JSTOR and Allette Systems for sponsoring its development.

The xsd2sch code is on GitHub.