Search code examples
xmlxsltxslt-2.0xsl-fo

How to reset list numbering in xslt


I have an xml file:

<ul class="ul">
<li class="li">UL LI 1</li>
<li class="li">UL LI 2</li>
<li class="li">UL LI 3
    <ol class="ol">
        <li class="li">OL LI 1</li>
        <li class="li">OL LI 2
            <ul>
                <li class="li">UL LI 1</li>
                <li class="li">UL LI 2
                    <ol class="ol">
                        <li class="li">OL LI 1</li>
                        <li class="li">OL LI 2
                            <ol class="ol">
                                <li class="li">OL LI 1</li>
                                <li class="li">OL LI 2</li>
                                <li class="li">OL LI 3</li>
                            </ol>
                        </li>
                    </ol>
                </li>
            </ul>
        </li>
    </ol>
</li>

What i want to achieve is something like this:

  • UL LI 1
  • UL LI 2
  • UL LI 3

    1. OL LI 1
    2. OL LI 2

      • UL LI 1
      • UL LI 2

        1. OL LI 1
        2. OL LI 2

          2.1 OL LI 1

          2.2 OL LI 2

          2.3 OL LI 3

If the parent of the ol is a ul, i want to reset the numbering, otherwise, the numbering must be inherited from it's parent. How can i do this in xslt?

I did try to solve this using this code:

                  <xsl:variable name="ol.parent" select="parent::*/parent::*/parent::*[contains(@class, ' ol ')]"/>

              <xsl:choose>
                <xsl:when test="not(empty($ol.parent))">
                  <xsl:number level="multiple" select="$ol.parent/ancestor-or-self::*[contains(@class, ' ol ')]" count="*[contains(@class, ' li ')]" format="1."/>
                </xsl:when>
                <xsl:otherwise>
                  <xsl:number level="multiple"  from="*[(contains(@class, ' ol ') or contains(@class, ' li '))]" format="1."/>
                </xsl:otherwise>
              </xsl:choose>

But it the ordered list doesn't inherit the multilevel number. It always starts with one.


Solution

  • I think you can use <xsl:number format="1." level="multiple" from="ul/li/ol"/> in the context of match="ol/li" e.g. https://xsltfiddle.liberty-development.net/ncdD7kN:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        exclude-result-prefixes="#all"
        version="3.0">
    
      <xsl:mode on-no-match="shallow-copy"/>
    
      <xsl:output method="html" indent="yes" html-version="5"/>
    
      <xsl:template match="ol/li">
          <xsl:copy>
              <xsl:number format="1." level="multiple" from="ul/li/ol"/>
              <xsl:apply-templates/>
          </xsl:copy>
      </xsl:template> 
    
    </xsl:stylesheet>
    

    outputs

    <ul class="ul">
    
       <li class="li">UL LI 1</li>
    
       <li class="li">UL LI 2</li>
    
       <li class="li">UL LI 3
    
          <ol class="ol">
    
             <li>1.OL LI 1</li>
    
             <li>2.OL LI 2
    
                <ul>
    
                   <li class="li">UL LI 1</li>
    
                   <li class="li">UL LI 2
    
                      <ol class="ol">
    
                         <li>1.OL LI 1</li>
    
                         <li>2.OL LI 2
    
                            <ol class="ol">
    
                               <li>2.1.OL LI 1</li>
    
                               <li>2.2.OL LI 2</li>
    
                               <li>2.3.OL LI 3</li>
    
                            </ol>
    
                         </li>
    
                      </ol>
    
                   </li>
    
                </ul>
    
             </li>
    
          </ol>
    
       </li>
    
    </ul>
    

    Of course the xsl:number use remains the same if instead of the above simple and quick XSLT 3 test case with HTML output you instead use XSLT 2 and create XSL-FO instead of HTML: https://xsltfiddle.liberty-development.net/ncdD7kN/1.