Search code examples
xsltxslt-2.0

How to li element move into the preceding-sibling 'p' element - XSLT


If following-sibling li element of p then how to move li element?
Input

<root>
<p>start</p>
<p>aaaaa</p>
<li>aaa</li>
<li>bbb</li>
<li>ccc</li>
<p>aaaaa</p>
<p>aaaaaa</p>
**Expected Output**
<root>
<p>start</p>
<p>aaaaa
    <li>aaa</li>
    <li>bbb</li>
    <li>ccc</li>
</p>
<p>aaaaa</p>
<p>aaaaaa</p>

XSLT:

    <xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="p">
    <p>
        <xsl:apply-templates/>
        <xsl:if test="following-sibling::*[1][self::li]">
            <xsl:for-each select="following-sibling::*[1][self::li]">
                <li><xsl:value-of select="."/></li>
            </xsl:for-each>
        </xsl:if>
    </p>
</xsl:template>

Solution

  • Because you can use XSLT-2.0, you can make use of xsl:for-each-group. So the following is one way to achieve the desired result:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
        <xsl:strip-space elements="*" />
        <xsl:output indent="yes" />
    
        <xsl:template match="@* | node()">
          <xsl:copy>
            <xsl:apply-templates select="@* | node()" />
          </xsl:copy>
        </xsl:template>
      
        <xsl:template match="p[name(following-sibling::*[1]) = 'li']">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()" />
                <xsl:for-each-group select="following-sibling::*" group-adjacent="name()">
                    <xsl:if test="position()=1">
                        <xsl:copy-of select="current-group()" />
                    </xsl:if>
                </xsl:for-each-group>
            </xsl:copy>
        </xsl:template>
      
        <xsl:template match="li[preceding-sibling::*[1] = (preceding-sibling::p[1] | preceding-sibling::li[1])]" />
      
    </xsl:stylesheet>
    

    The output is:

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
       <p>start</p>
       <p>aaaaa<li>aaa</li>
          <li>bbb</li>
          <li>ccc</li>
       </p>
       <p>aaaaa</p>
       <p>aaaaaa</p>
    </root>