Search code examples
xsltxslt-2.0xsl-grouping

xsl:for-each-group not working as per exception


I have to transform XML tag out of p as separate div but i am unable to do it:

XML:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <p>
        This is <bold>First</bold> paragraph  
        <boxed-text>
            <sec>
                <title>Boxed title</title>
                <p>
                    <list list-type="bullet">
                        <list-item>
                            <p>List first para</p>
                        </list-item>
                    </list>
                </p>
            </sec>
        </boxed-text>
        This is <bold>Second</bold> paragraph  
    </p>

</root>

XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    <xsl:output method="xhtml" indent="yes"/>
    <xsl:strip-space elements="*"/>

<xsl:template match="/">
    <html>
        <head>
            <title>Title</title>
        </head>
        <body>
            <xsl:apply-templates/>
        </body>
    </html>
</xsl:template>

    <xsl:template match="p">
        <xsl:choose>
            <xsl:when test="descendant::boxed-text">
                <xsl:for-each-group select="node()" group-starting-with="node()">
                    <xsl:choose>
                        <xsl:when test="self::boxed-text">
                            <div class="boxed-text">
                                <xsl:apply-templates select="current-group()"/>
                            </div>
                        </xsl:when>
                        <xsl:otherwise>
                            <p class="indent">
                                <xsl:apply-templates select="current-group()"/>
                            </p>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each-group>
            </xsl:when>
            <xsl:otherwise>
                <p class="indent">
                    <xsl:apply-templates/>
                </p>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="boxed-text">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="p[table-wrap | list]">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="list">
        <xsl:choose>
            <xsl:when test="@list-type[. = 'bullet']">
                <ol style="list-style-type:disc;">
                    <xsl:apply-templates/>
                </ol>
            </xsl:when>
            <xsl:otherwise>
                <ol style="list-style-type:none;">
                    <xsl:apply-templates/>
                </ol>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="list-item">
        <li>
            <xsl:apply-templates/>
        </li>
    </xsl:template>

</xsl:stylesheet>

Current Output:

<?xml version="1.0" encoding="UTF-8"?><html>
   <head>
      <title>Title</title>
   </head>
   <body>
      <p class="indent">
         This is 
      </p>
      <p class="indent">First</p>
      <p class="indent"> paragraph  

      </p>
      <div class="boxed-text">Boxed title
         <ol style="list-style-type:disc;">
            <li>
               <p class="indent">List first para</p>
            </li>
         </ol>
      </div>
      <p class="indent">
         This is 
      </p>
      <p class="indent">Second</p>
      <p class="indent"> paragraph  

      </p>
   </body>
</html>

Excepted output:

<?xml version="1.0" encoding="UTF-8"?><html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <p class="indent">This is <b>First</b> paragraph</p>
        <div class="boxed-text">Boxed title
            <ol style="list-style-type:disc;">
                <li>
                    <p class="indent">List first para</p>
                </li>
            </ol>
        </div>
        <p class="indent">This is <b>Second</b> paragraph</p>
    </body>
</html>

Solution

  • It seems this is rather a job for group-adjacent="boolean(self::boxed-text)":

    <xsl:template match="p[descendant::boxed-text]">
        <xsl:for-each-group select="node()" group-adjacent="boolean(self::boxed-text)">
            <xsl:choose>
                <xsl:when test="current-grouping-key()">
                    <xsl:apply-templates select="current-group()"/>
                </xsl:when>
                <xsl:otherwise>
                    <p class="indent">
                        <xsl:apply-templates select="current-group()"/>
                    </p>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:template>
    

    Complete adaption is

    <?xml version="1.0" encoding="UTF-8"?>
    <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:output method="html" indent="no" html-version="5"/>
    
      <xsl:template match="/">
        <html>
          <head>
            <title>.NET XSLT Fiddle Example</title>
          </head>
          <body>
            <xsl:apply-templates/>
          </body>
        </html>
      </xsl:template>
    
        <xsl:template match="p">
            <p class="indent">
                <xsl:apply-templates/>
            </p>
        </xsl:template>
    
        <xsl:template match="p[descendant::boxed-text]">
            <xsl:for-each-group select="node()" group-adjacent="boolean(self::boxed-text)">
                <xsl:choose>
                    <xsl:when test="current-grouping-key()">
                        <xsl:apply-templates select="current-group()"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <p class="indent">
                            <xsl:apply-templates select="current-group()"/>
                        </p>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </xsl:template>
    
        <xsl:template match="boxed-text">
            <div class="boxed-text">
                <xsl:apply-templates/>
            </div>
        </xsl:template>
    
        <xsl:template match="p[table-wrap | list]">
            <xsl:apply-templates/>
        </xsl:template>
    
        <xsl:template match="list">
            <ol style="list-style-type:none;">
                <xsl:apply-templates/>
            </ol>
        </xsl:template>
    
        <xsl:template match="list[@list-type[. = 'bullet']]">
            <ol style="list-style-type:disc;">
                <xsl:apply-templates/>
            </ol>
        </xsl:template>
    
        <xsl:template match="list-item">
            <li>
                <xsl:apply-templates/>
            </li>
        </xsl:template>
    
    </xsl:stylesheet>
    

    https://xsltfiddle.liberty-development.net/6rewNyg