Search code examples
htmlxmlxsltgroupingxslt-2.0

Grouping adjacent elements with different classes


I want to group elements with @class="warning" and with @class="warninglistbullet" into a single .

Input:

<html>
  <head>
      <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
      <title>Title</title>
  </head>
  <body>
      <section>
         <h1 class="heading1">A section</h1>
         <p class="bodytext">Some bodytext.</p>
         <p class="parts">1234</p>
         <p class="parts">23456</p>
         <p class="parts">2341</p>
         <p class="bodytext">Some bodytext.</p>
         <p class="warning">Eletrical hazard</p> 
         <p class="warning">Do not:</p> 
         <ul class="warninglistbullet">
             <li class="warninglistbullet">
                 <p class="warninglistbullet">Take a bath and</p>
             </li>
             <li class="warninglistbullet">
                 <p class="warninglistbullet">use power tools at the same time.</p>
             </li>
         </ul>
      </section>
   </body>
</html>

Desired output:

<html>
  <head>
      <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
      <title>Title</title>
  </head>
  <body>
      <section>
         <h1 class="heading1">A section</h1>
         <p class="bodytext">Some bodytext.</p>
         <div class="parts">
            <p class="parts">1234</p>
            <p class="parts">23456</p>
            <p class="parts">2341</p>
         </div>
         <p class="bodytext">Some bodytext.</p>
         <div class="warning">
            <p class="warning">Eletrical hazard</p> 
            <p class="warning">Do not:</p> 
            <ul class="warninglistbullet">
               <li class="warninglistbullet">
                 <p class="warninglistbullet">Take a bath and</p>
               </li>
               <li class="warninglistbullet">
                 <p class="warninglistbullet">use power tools at the same time.</p>
               </li>
            </ul>
         </div>
      </section>
   </body>
</html>

Current, wrong template:

<xsl:template match="section">
        <xsl:element name="{local-name()}">
            <xsl:apply-templates select="@*"/>
            <xsl:for-each-group select="*[@class!='']" group-adjacent="@class">
                    <xsl:choose>
                        <xsl:when test="current-grouping-key = 'parts'">
                            <div class="parts">
                                <xsl:apply-templates select="current-group()"/>
                            </div>
                        </xsl:when>
                        <xsl:when test="(current-grouping-key = 'warning') or (current-grouping-key = 'warninglistbullet')">
                            <div class="warning">
                                <xsl:apply-templates select="current-group()"/>
                            </div>        
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:apply-templates select="current-group()"/>
                        </xsl:otherwise>
                    </xsl:choose>
            </xsl:for-each-group>
        </xsl:element>
    </xsl:template>

I am able to group together elements with the same class, but how to group elements with different classes?

--

Apparently, Stackoverflow will not let me post the question because there is too much code and too little explanation.

So I will ramble on just to please the script that is preventing me from posting.

I tried both the "boolean" pattern and the "@class" pattern in the for-each-group element. I was not able to get either to work:

  • I do not know how to write the boolean pattern to match "parts" and "warning" and "warninglistbullet".
  • If I try to group-by class and evaluate the current-grouping-key() as "warning" OR "warningbulletitem", then the two elements never end up being grouped together.

Solution

  • How about:

    <xsl:template match="section">
        <xsl:copy>
            <xsl:for-each-group select="*[@class!='']" group-adjacent="replace(@class, 'warninglistbullet', 'warning')">
                <xsl:choose>
                    <xsl:when test="current-grouping-key() = ('parts', 'warning')">
                        <div class="{current-grouping-key()}">
                            <xsl:apply-templates select="current-group()"/>
                        </div>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:apply-templates select="current-group()"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>