Joe Mierwa
Joe Mierwa

Reputation: 23

Schematron Using Include Fails With Ambiguous rule match

I am running the core schematron XSLT (as downloaded from http://schematron.com/front-page/the-schematron-skeleton-implementation/) against my schematron rules which have an include statement. When trying to run the XML instance to be tested through the intermediate XSL, it fails with 'Ambiguous rule match for' for each rule present in the included schematron.

"Description: Ambiguous rule match for /filing:FilingMessage/filing:FilingConnectedDocument[1]/ecf:DocumentAugmentation[1]/ecf:DocumentRendition[1]/nc:Attachment[1]/nc:BinaryFormatText[1] Matches both "{http://release.niem.gov/niem/niem-core/3.0/}BinaryFormatText" on line 192 of file:/C:/_working/misc/schematron/schematron/trunk/schematron/code/temp.xsl and "{http://release.niem.gov/niem/niem-core/3.0/}BinaryFormatText" on line 175 of file:/C:/_working/misc/schematron/schematron/trunk/schematron/code/temp.xsl URL: http://www.w3.org/TR/xslt20/#err-XTRE0540".

Upon inspecting the resulting intermediate XSL, it appears that the rules contained in the included schematron file are rendered into the intermediate XSL twice. Inspecting the XSL file iso_schematron_skeleton_for_saxon.xsl, it looks like the iso:include calls the rules template with both the pattern node and the rules node which results in the duplication of the data.

I would have assumed that the schematron XSLT on github is the definitive implementation of the schematron specification. Is that not the case or can anyone comment to this as I don't feel it is correct for me to have to tweak the schematron XSLT to make it work?

Base Schematron

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">
    <title>Test Schematron Illustrating Transform Bug</title>

    <ns prefix="x" uri="http://www.w3.org/TR/REC-html40"/>
    <ns prefix="filing" uri="https://docs.oasis-open.org/legalxml-courtfiling/ns/v5.0/filing"/>
    <ns prefix="nc" uri="http://release.niem.gov/niem/niem-core/3.0/"/>

    <include href="ExternalTestPattern.sch"/>

    <pattern id="ecf">
        <rule context="/filing:FilingMessage">
            <assert test="./nc:DocumentIdentification/nc:IdentificationID">DocumentID must be present.</assert>
        </rule>
    </pattern>

</schema>

ExternalTestPattern.sch Schematron

<?xml version="1.0" encoding="UTF-8"?>
<pattern xmlns="http://purl.oclc.org/dsdl/schematron" id="code-list-rules">
<!-- Required namespace declarations as indicated in this set of rules:
     <ns prefix="nc" uri="http://release.niem.gov/niem/niem-core/3.0/"/> -->

   <rule context="nc:BinaryFormatText">
      <assert test="( false() or ( contains('&#127;application/json&#127;application/msword&#127;application/pdf&#127;application/vnd.oasis.opendocument.text&#127;application/vnd.openxmlformats-officedocument.wordprocessingml.document&#127;application/xml&#127;',concat('&#127;',.,'&#127;')) ) ) ">Invalid binary format code value.</assert>
   </rule>
</pattern>

One other thing I forgot to mention is that if I simply use an XML editor like Oxygen or XML buddy to use my schematron to validate the XML instance, it works fine.

When I use Oxygen to load my Test.sch and also iso_schematron_message_xslt2.xslt, the resulting XSLT is below. Partway down you will see that the template for the rule defined in ExternalTestPattern.sch (match="nc:BinaryFormatText") is duplicated:

<xsl:stylesheet xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:saxon="http://saxon.sf.net/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:schold="http://www.ascc.net/xml/schematron" xmlns:iso="http://purl.oclc.org/dsdl/schematron" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:x="http://www.w3.org/TR/REC-html40" xmlns:filing="https://docs.oasis-open.org/legalxml-courtfiling/ns/v5.0/filing" xmlns:nc="http://release.niem.gov/niem/niem-core/3.0/" version="2.0">
<!--Implementers: please note that overriding process-prolog or process-root is the preferred method for meta-stylesheets to use where possible. -->
<xsl:param name="archiveDirParameter"/>
   <xsl:param name="archiveNameParameter"/>
   <xsl:param name="fileNameParameter"/>
   <xsl:param name="fileDirParameter"/>
   <xsl:variable name="document-uri">
      <xsl:value-of select="document-uri(/)"/>
   </xsl:variable>

   <!--PHASES-->


<!--PROLOG-->
<xsl:output method="text"/>

   <!--XSD TYPES FOR XSLT2-->


<!--KEYS AND FUNCTIONS-->


<!--DEFAULT RULES-->


<!--MODE: SCHEMATRON-SELECT-FULL-PATH-->
<!--This mode can be used to generate an ugly though full XPath for locators-->
<xsl:template match="*" mode="schematron-select-full-path">
      <xsl:apply-templates select="." mode="schematron-get-full-path"/>
   </xsl:template>

   <!--MODE: SCHEMATRON-FULL-PATH-->
<!--This mode can be used to generate an ugly though full XPath for locators-->
<xsl:template match="*" mode="schematron-get-full-path">
      <xsl:apply-templates select="parent::*" mode="schematron-get-full-path"/>
      <xsl:text>/</xsl:text>
      <xsl:choose>
         <xsl:when test="namespace-uri()=''">
            <xsl:value-of select="name()"/>
         </xsl:when>
         <xsl:otherwise>
            <xsl:text>*:</xsl:text>
            <xsl:value-of select="local-name()"/>
            <xsl:text>[namespace-uri()='</xsl:text>
            <xsl:value-of select="namespace-uri()"/>
            <xsl:text>']</xsl:text>
         </xsl:otherwise>
      </xsl:choose>
      <xsl:variable name="preceding" select="count(preceding-sibling::*[local-name()=local-name(current()) and namespace-uri() = namespace-uri(current())])"/>
      <xsl:text>[</xsl:text>
      <xsl:value-of select="1+ $preceding"/>
      <xsl:text>]</xsl:text>
   </xsl:template>
   <xsl:template match="@*" mode="schematron-get-full-path">
      <xsl:apply-templates select="parent::*" mode="schematron-get-full-path"/>
      <xsl:text>/</xsl:text>
      <xsl:choose>
         <xsl:when test="namespace-uri()=''">@<xsl:value-of select="name()"/>
         </xsl:when>
         <xsl:otherwise>
            <xsl:text>@*[local-name()='</xsl:text>
            <xsl:value-of select="local-name()"/>
            <xsl:text>' and namespace-uri()='</xsl:text>
            <xsl:value-of select="namespace-uri()"/>
            <xsl:text>']</xsl:text>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>

   <!--MODE: SCHEMATRON-FULL-PATH-2-->
<!--This mode can be used to generate prefixed XPath for humans-->
<xsl:template match="node() | @*" mode="schematron-get-full-path-2">
      <xsl:for-each select="ancestor-or-self::*">
         <xsl:text>/</xsl:text>
         <xsl:value-of select="name(.)"/>
         <xsl:if test="preceding-sibling::*[name(.)=name(current())]">
            <xsl:text>[</xsl:text>
            <xsl:value-of select="count(preceding-sibling::*[name(.)=name(current())])+1"/>
            <xsl:text>]</xsl:text>
         </xsl:if>
      </xsl:for-each>
      <xsl:if test="not(self::*)">
         <xsl:text/>/@<xsl:value-of select="name(.)"/>
      </xsl:if>
   </xsl:template>
   <!--MODE: SCHEMATRON-FULL-PATH-3-->
<!--This mode can be used to generate prefixed XPath for humans 
    (Top-level element has index)-->
<xsl:template match="node() | @*" mode="schematron-get-full-path-3">
      <xsl:for-each select="ancestor-or-self::*">
         <xsl:text>/</xsl:text>
         <xsl:value-of select="name(.)"/>
         <xsl:if test="parent::*">
            <xsl:text>[</xsl:text>
            <xsl:value-of select="count(preceding-sibling::*[name(.)=name(current())])+1"/>
            <xsl:text>]</xsl:text>
         </xsl:if>
      </xsl:for-each>
      <xsl:if test="not(self::*)">
         <xsl:text/>/@<xsl:value-of select="name(.)"/>
      </xsl:if>
   </xsl:template>

   <!--MODE: GENERATE-ID-FROM-PATH -->
<xsl:template match="/" mode="generate-id-from-path"/>
   <xsl:template match="text()" mode="generate-id-from-path">
      <xsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
      <xsl:value-of select="concat('.text-', 1+count(preceding-sibling::text()), '-')"/>
   </xsl:template>
   <xsl:template match="comment()" mode="generate-id-from-path">
      <xsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
      <xsl:value-of select="concat('.comment-', 1+count(preceding-sibling::comment()), '-')"/>
   </xsl:template>
   <xsl:template match="processing-instruction()" mode="generate-id-from-path">
      <xsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
      <xsl:value-of select="concat('.processing-instruction-', 1+count(preceding-sibling::processing-instruction()), '-')"/>
   </xsl:template>
   <xsl:template match="@*" mode="generate-id-from-path">
      <xsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
      <xsl:value-of select="concat('.@', name())"/>
   </xsl:template>
   <xsl:template match="*" mode="generate-id-from-path" priority="-0.5">
      <xsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
      <xsl:text>.</xsl:text>
      <xsl:value-of select="concat('.',name(),'-',1+count(preceding-sibling::*[name()=name(current())]),'-')"/>
   </xsl:template>

   <!--MODE: GENERATE-ID-2 -->
<xsl:template match="/" mode="generate-id-2">U</xsl:template>
   <xsl:template match="*" mode="generate-id-2" priority="2">
      <xsl:text>U</xsl:text>
      <xsl:number level="multiple" count="*"/>
   </xsl:template>
   <xsl:template match="node()" mode="generate-id-2">
      <xsl:text>U.</xsl:text>
      <xsl:number level="multiple" count="*"/>
      <xsl:text>n</xsl:text>
      <xsl:number count="node()"/>
   </xsl:template>
   <xsl:template match="@*" mode="generate-id-2">
      <xsl:text>U.</xsl:text>
      <xsl:number level="multiple" count="*"/>
      <xsl:text>_</xsl:text>
      <xsl:value-of select="string-length(local-name(.))"/>
      <xsl:text>_</xsl:text>
      <xsl:value-of select="translate(name(),':','.')"/>
   </xsl:template>
   <!--Strip characters--><xsl:template match="text()" priority="-1"/>

   <!--SCHEMA SETUP-->
<xsl:template match="/">
      <xsl:apply-templates select="/" mode="M0"/>
      <xsl:apply-templates select="/" mode="M5"/>
   </xsl:template>

   <!--SCHEMATRON PATTERNS-->


<!--PATTERN code-list-rules-->


    <!--RULE -->
<xsl:template match="nc:BinaryFormatText" priority="1000" mode="M0">

        <!--ASSERT -->
<xsl:choose>
         <xsl:when test="( false() or ( contains('&#127;application/json&#127;application/msword&#127;application/pdf&#127;application/vnd.oasis.opendocument.text&#127;application/vnd.openxmlformats-officedocument.wordprocessingml.document&#127;application/xml&#127;',concat('&#127;',.,'&#127;')) ) ) "/>
         <xsl:otherwise>
            <xsl:message>Invalid binary format code value. (( false() or ( contains('&#127;application/json&#127;application/msword&#127;application/pdf&#127;application/vnd.oasis.opendocument.text&#127;application/vnd.openxmlformats-officedocument.wordprocessingml.document&#127;application/xml&#127;',concat('&#127;',.,'&#127;')) ) ))</xsl:message>
         </xsl:otherwise>
      </xsl:choose>
      <xsl:apply-templates select="*|comment()|processing-instruction()" mode="M0"/>
   </xsl:template>
   <xsl:template match="text()" priority="-1" mode="M0"/>
   <xsl:template match="@*|node()" priority="-2" mode="M0">
      <xsl:apply-templates select="*|comment()|processing-instruction()" mode="M0"/>
   </xsl:template>

      <!--RULE -->
<xsl:template match="nc:BinaryFormatText" priority="1000" mode="M0">

        <!--ASSERT -->
<xsl:choose>
         <xsl:when test="( false() or ( contains('&#127;application/json&#127;application/msword&#127;application/pdf&#127;application/vnd.oasis.opendocument.text&#127;application/vnd.openxmlformats-officedocument.wordprocessingml.document&#127;application/xml&#127;',concat('&#127;',.,'&#127;')) ) ) "/>
         <xsl:otherwise>
            <xsl:message>Invalid binary format code value. (( false() or ( contains('&#127;application/json&#127;application/msword&#127;application/pdf&#127;application/vnd.oasis.opendocument.text&#127;application/vnd.openxmlformats-officedocument.wordprocessingml.document&#127;application/xml&#127;',concat('&#127;',.,'&#127;')) ) ))</xsl:message>
         </xsl:otherwise>
      </xsl:choose>
      <xsl:apply-templates select="*|comment()|processing-instruction()" mode="M0"/>
   </xsl:template>

   <!--PATTERN ecf-->


    <!--RULE -->
<xsl:template match="/filing:FilingMessage" priority="1000" mode="M5">

        <!--ASSERT -->
    <xsl:choose>
         <xsl:when test="./nc:DocumentIdentification/nc:IdentificationID"/>
         <xsl:otherwise>
            <xsl:message>DocumentID must be present. (./nc:DocumentIdentification/nc:IdentificationID)</xsl:message>
         </xsl:otherwise>
      </xsl:choose>
      <xsl:apply-templates select="*|comment()|processing-instruction()" mode="M5"/>
   </xsl:template>
   <xsl:template match="text()" priority="-1" mode="M5"/>
   <xsl:template match="@*|node()" priority="-2" mode="M5">
      <xsl:apply-templates select="*|comment()|processing-instruction()" mode="M5"/>
   </xsl:template>
</xsl:stylesheet>

Any enlightenment would be really helpful. Thanks

Upvotes: 2

Views: 784

Answers (1)

Mads Hansen
Mads Hansen

Reputation: 66714

Ensure that you are compiling the Schematron into XSLT by applying the sequence of transformations outlined in the readme, using the output of the prior XSLT transforms as the input to the next step:

1) First, preprocess your Schematron schema with iso_dsdl_include.xsl. This is a macro processor to assemble the schema from various parts. If your schema is not in separate parts, you can skip this stage. This stage also generates error messages for some common XPath syntax problems.

2) Second, preprocess the output from stage 1 with iso_abstract_expand.xsl. This is a macro processor to convert abstract patterns to real patterns. If your schema does not use abstract patterns, you can skip this stage.

3) Third, compile the Schematron schema into an XSLT script. This will typically use iso_svrl_for_xslt1.xsl or iso_svrl_for_xslt2.xsl (which in turn invoke iso_schematron_skeleton_for_xslt1.xsl or iso_schematron_skeleton_for_saxon.xsl) However, other "meta-stylesheets" are also in common use; the principle of operation is the same. If your schema uses Schematron phases, supply these as command line/invocation parameters to this process.

4) Fourth, run the script generated by stage 3 against the document being validated. If you are using the SVRL script, then the output of validation will be an XML document. If your schema uses Schematron parameters, supply these as command line/invocation parameters to this process.

Also, ensure that you are applying iso_svrl_for_xslt2.xsl (which imports iso_schematron_skeleton_for_saxon.xsl) and are not using iso_schematron_skeleton_for_saxon.xsl directly

Upvotes: 0

Related Questions