Search code examples
xsltgroupingxslt-2.0xsl-grouping

Grouping several groups in XSLT 2


I'm trying to add hierarchy to some grotty extruded typesetting XML. I can't seem to manage grouping several kinds of groups in the same parent element at once.

What I have (simplified, obviously):

<article>
  <h1>A section title here</h1>
  <p>A paragraph.</p>
  <p>Another paragraph.</p>
  <bl>Bulleted list item.</bl>
  <bl>Another bulleted list item.</bl>
  <h1>Another section title</h1>
  <p>Yet another paragraph.</p>
</article>

What I want:

<article>
  <sec>
    <h1>A section title here</h1>
    <p>A paragraph.</p>
    <p>Another paragraph.</p>
    <list>
      <list-item>Bulleted list item.</list-item>
      <list-item>Another bulleted list item.</list-item>
    </list>
  </sec>
  <sec>
    <h1>Another section title</h1>
    <p>Yet another paragraph.</p>
  </sec>
</article>

This almost works for the list items:

<xsl:for-each-group select="*" group-adjacent="boolean(self::BL)">
   <xsl:choose>
      <xsl:when test="current-grouping-key()">
         <list><xsl:apply-templates select="current-group()"/></list>
      </xsl:when>
      <xsl:otherwise>
         <xsl:apply-templates select="current-group()"/>
            </xsl:otherwise>
   </xsl:choose>
 </xsl:for-each-group>

but it only handles the very first list in an article; and as soon as I try to add another xsl:for-each-group to cover the sections, the list-item one stops working.

Ideas? Many thanks in advance!


Solution

  • Here is a sample stylesheet that produces the output you posted for the input sample you posted:

    <xsl:stylesheet
      version="2.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
      <xsl:strip-space elements="*"/>
      <xsl:output indent="yes"/>
    
      <xsl:template match="article">
        <xsl:copy>
          <xsl:for-each-group select="*" group-starting-with="h1">
            <sec>
              <xsl:copy-of select="."/>
              <xsl:for-each-group select="current-group() except ." group-adjacent="boolean(self::bl)">
                <xsl:choose>
                  <xsl:when test="current-grouping-key()">
                    <list>
                      <xsl:apply-templates select="current-group()"/>
                    </list>
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:copy-of select="current-group()"/>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:for-each-group>
            </sec>
          </xsl:for-each-group>
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="bl">
        <list-item>
          <xsl:apply-templates/>
        </list-item>
      </xsl:template>
    
    </xsl:stylesheet>