Search code examples
xslt-2.0

XSLT How to copy following elements inside the new group


I have this flat xml. i need to group the contents on h1/title and copy all following para's until next h1/title pattern if exists else add empty para.

Source XML:

 <Element>
    <h1>
        <title>Name1</title>
    </h1>
    <h1>
        <title>Name2</title>
    </h1>
    <para>Test1</para>
    <para>Test2</para>
    <h1>
        <title>Name3</title>
    </h1>
    <para>Test3</para>
    <para>Test4</para>
</Element>

I want the output like below.

<Element>
    <group>
    <h1>
        <title>Name1</title>
    </h1>
    <para> </para>
    </group>
    <group>
    <h1>
        <title>Name2</title>
    </h1>
    <para>Test1</para>
    <para>Test2</para>
    </group>
    <group>
    <h1>
        <title>Name3</title>
    </h1>
    <para>Test3</para>
    <para>Test4</para>
    </group>
</Element>

so far I have tried following template, it does not copy following para's.

<xsl:template match="h1">
    <group>
        <xsl:copy>
        <xsl:for-each-group select="*" group-starting-with="h1">
            <xsl:choose>
                <xsl:when test="self::h1">
                    <group>
                        <xsl:apply-templates select="current-group()"/>
                        <xsl:apply-templates select="following-sibling::para[not(following::h1)]"/>
                    </group>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="current-group()"/>  
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
        </xsl:copy>
    </group>
</xsl:template>

Solution

  • I would suggest to group the children of the Element element and then of course inside of the for-each-group you can simply check whether there is no second group item to add the empty para:

    <?xml version="1.0" encoding="UTF-8" ?>
    <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    
        <xsl:output indent="yes"/>
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="Element">
            <xsl:copy>
                <xsl:for-each-group select="*" group-starting-with="h1">
                    <xsl:choose>
                        <xsl:when test="self::h1">
                            <group>
                                <xsl:apply-templates select="current-group()"/>
                                <xsl:if test="not(current-group()[2])">
                                    <para/>
                                </xsl:if>
                            </group>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:apply-templates select="current-group()"/>  
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each-group>
            </xsl:copy>
        </xsl:template>
    
    </xsl:transform>
    

    Online at http://xsltransform.net/naZXpX7.