xslt-3.0

Check numbering of list items with XSLT


I have got lists with two types of list markers, namely numeric (1., 2., 3., etc.) and alphabetic [a), b), c), etc. or aa), bb), cc), etc.]. I want to check whether each item of a list is numbered subsequently according to its list marker type.

This means in numeric lists a subsequent item should be numbered as n+1 plus a dot, where n is the number in the current item's term and n+1 would be the number in its following-sibling::*[1]/self::item/term.

In alphabetic lists the following-sibling::*[1]/self::item/term should contain the next letter(s) in alphabetical order from the current item/term plus a closing parenthesis. So for example after "b)" would follow "c)" and after "cc)" would follow "dd)".

I do not want to change the numbering using xsl:number, but just check and document the errors found and ideally their locations.

The input format would look like this:

<list>
  <item>
    <term>1.</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
  <item>
    <term>3.</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
  <item>
    <term>4.</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
</list>

Or:

<list>
  <item>
    <term>a)</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
  <item>
    <term>b)</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
  <item>
   <term>d)</term>
   <p>jhjhjh hjkjkjkj</p>
  </item>
</list>

Or:

<list>
  <item>
    <term>aa)</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
  <item>
    <term>cc)</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
  <item>
    <term>dd)</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
</list>

These input samples deliberately contain errors that should be detected and located.


Solution

  • Seems like a task for Schematron e.g.

    <schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt3">
        <pattern>
            <rule context="item[term[matches(., '^[0-9]+\.$')]][position() gt 1]">
                <assert test="
                let $number := term => substring-before('.') => xs:integer(), 
                $number2 := preceding-sibling::item[1]/term => substring-before('.') => xs:integer() 
                return $number = $number2 + 1">item with <value-of select="term"/> doesn't have the right number.</assert>
            </rule>
        </pattern>
    </schema>
    

    Sample fiddle.

    For your alphabetic schema perhaps the following

    <schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt3">
        <pattern>
            <rule context="item[term[matches(., '^[0-9]+\.$')]][position() gt 1]">
                <assert test="
                let $number := term => substring-before('.') => xs:integer(), 
                $number2 := preceding-sibling::item[1]/term => substring-before('.') => xs:integer() 
                return $number = $number2 + 1">item with <value-of select="term"/> doesn't have the right number.</assert>
            </rule>
            <rule context="item[term[matches(., '^[a-z]+\)$')]][position() gt 1]">
                <assert test="
                let $index := term => substring-before(')') => string-to-codepoints(), 
                $index2 := preceding-sibling::item[1]/term => substring-before(')') => string-to-codepoints()
                return every $bool in for-each-pair($index, $index2, function($a, $b) { $a = $b + 1 }) satisfies $bool">item with <value-of select="term"/> doesn't have the right term.</assert>
            </rule>
        </pattern>
    </schema>
    

    helps, sample fiddle is online.

    As for the error with Schxslt2, I think it is a bug in the template schxslt:failed-assertion-content of transpile.xsl that can be fixed as

      <xsl:template name="schxslt:failed-assertion-content" as="node()+">
        <xsl:sequence select="@flag"/>
        <xsl:sequence select="@id"/>
        <xsl:sequence select="@role"/>
        <xsl:attribute name="test" select="@test => replace('\{', '{{') => replace('\}', '}}')"/>
        <xsl:attribute name="xml:lang" select="schxslt:in-scope-language(.)"/>
        <alias:attribute name="location" select="{($schxslt:location-function, 'path')[1]}(.)" xsl:use-when="not($schxslt:streamable) or exists($schxslt:location-function)"/>
        <xsl:call-template name="schxslt:report-diagnostics"/>
        <xsl:call-template name="schxslt:report-properties"/>
        <xsl:call-template name="schxslt:report-message"/>
      </xsl:template>
    

    Bug is already fixed in Schxslt2 1.2.2, I have incorporated that version as an option in the Schematron fiddle, the sample now works.