xmlschematronrelaxng

Schematron Assert rule embedded in RelaxNG produces undeclared namespace prefix error


Example code

Consider the following somewhat contrived XML instance:

file.xml

<?xml version="1.0" encoding="UTF-8"?>
<section xmlns:aid="http://ns.adobe.com/AdobeInDesign/4.0/" xmlns:aid5="http://ns.adobe.com/AdobeInDesign/5.0/">
  <table aid:trows="3" aid:tcols="2">
    <Cell>header column 1</Cell>
    <Cell>header column 2</Cell>
    <Cell>row 1 column 1</Cell>
    <Cell>row 1 column 2</Cell>
    <Cell>row 2 column 1</Cell>
    <Cell>row 2 column 2</Cell>
  </table>
</section>

It successfully validates using the following Relax NG Schema:

schema.rng

<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
  datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
  xmlns:sch="http://purl.oclc.org/dsdl/schematron"
  xmlns:aid="http://ns.adobe.com/AdobeInDesign/4.0/"
  xmlns:aid5="http://ns.adobe.com/AdobeInDesign/5.0/">
  <start>
    <ref name="section"/>
  </start>
  <define name="section">
    <element name="section">
      <ref name="table"/>
    </element>
  </define>
  <define name="table">
    <sch:pattern name="Test attributes aid:rows and aid:tcols when multiplied equal count of Cell elements">
      <sch:rule context="table">
        <sch:assert test="count(./Cell) = 6">Count of Cell elements does not match specified @aid:rows and @aid:tcols.</sch:assert>
      </sch:rule>
    </sch:pattern>
    <element name="table">
      <attribute name="aid:trows">
        <data type="integer"/>
      </attribute>
      <attribute name="aid:tcols">
        <data type="integer"/>
      </attribute>
      <oneOrMore>
        <element name="Cell">
          <text/>
        </element>
      </oneOrMore>
    </element>
  </define>
</grammar>

Note: the embedded Schematron rule in schema.rng, i.e. the part that reads as follows:

<sch:pattern name="Test attributes aid:rows and aid:tcols when multiplied equal count of Cell elements">
 <sch:rule context="table">
   <sch:assert test="count(./Cell) = 6">Count of Cell elements does not match specified @aid:rows and @aid:tcols.</sch:assert>
 </sch:rule>
</sch:pattern>

This rule assert's that the count of Cell element nodes must equal 6:

What am I trying to do?

I'm trying to avoid hard coding what the count of Cell elements must be in the XPath count function that is currently defined in the Schematron rule. i.e. this part (shown again below) stating that the count of Cell elements must be six:

<sch:assert test="count(./Cell) = 6">...</sch:assert>
                                  ^

What I want to do is infer what the count of Cell elements must be by multiplying the values of the aid:trows and aid:tcols attributes associated with the table element. i.e. infer it from these attributes in file.xml shown again below:

<table aid:trows="3" aid:tcols="2">
                  ^             ^
  ...
</table>

What have I tried...

I've tried redefining the XPath count function in the Schematron rule as follows.

<sch:assert test="count(./Cell) = @aid:trows * @aid:tcols">...</sch:assert>

However, attempting to validate file.xml produces the following error (btw. I'm using Oxygen XML Editor):

Failed to compile stylesheet. 1 error detected.
Undeclared namespace prefix {aid}
Got a fatal error trying to create a transformer from the stylesheet!

There seems to be some issue with the aid namespace prefix!

What am I misunderstanding here? How can I fix this?

What does work?

To determine whether it's aid namespace prefix related I tried the following which does work successfully:

  1. In file.xml I omitted the aid prefix from the aid:trows and aid:tcol attributes associated with the table as shown below:

    ...
    <table trows="3" tcols="2">
        ...
    </table>
    
  2. Then redefined:

    • the XPath count function in the Schematron rule as count(./Cell) = @trows * @tcols
    • and defined the attributes for the table as trows and tcols, i.e. omitted the aid: prefixes.

    Essentially I redefined the named pattern for table to the following:

    ...
    <define name="table">
      <sch:pattern name="Test attributes aid:rows and aid:tcols when multiplied equal count of Cell elements">
        <sch:rule context="table">
          <!-- change below -->
          <sch:assert test="count(./Cell) = @trows * @tcols">Count of Cell elements does not match specified @aid:rows and @aid:tcols.</sch:assert>
        </sch:rule>
      </sch:pattern>
      <element name="table">
        <!-- change below -->
        <attribute name="trows">
          <data type="integer"/>
        </attribute>
        <!-- change below -->
        <attribute name="tcols">
          <data type="integer"/>
        </attribute>
        <oneOrMore>
          <element name="Cell">
            <text/>
          </element>
        </oneOrMore>
      </element>
    </define>
    ...
    

Solution

  • Here are some options:

    1. Namespace prefixes used in assert tests in Schemetron must be declared using the Schematron ns element. Add the following as the first child within <grammar> in the RelaxNG schema:

      <sch:ns prefix="aid" uri="http://ns.adobe.com/AdobeInDesign/4.0/"/>

    2. Alternatively, you can assert the namespace of the attributes within the assert test by using the xPath namespace-uri() function. Change:

      @aid:trows

      to:

      @*:trows[namespace-uri() = 'http://ns.adobe.com/AdobeInDesign/4.0/']

      (and the same for tcols).

    3. Or, if you're lazy, you can use @*:trows, which will select trows attributes of any namespace.