Search code examples
xmlxsltxpathtransformationflat

XSLT a flat list to a tree hierarchy


I recently ran into a problem I havent found a good solution for yet.

Iam given a XML file with a flat hierarchy and want to transform it into a defined hierarchy.

<xml_file>
<dd/>
<b/>
<b/>
<dd/>
<b/>
<b/>
<dd/>
<k/>
<b/>
<b/>
<dd/>
<b/>
<b/>
</xml_file>

What I want to turn it into:

<xml_file>
    <dd>
        <b/>
        <b/>
    </dd>
    <dd>
        <b/>
        <b/>
    </dd>
    <dd>
        <k>
           <b/>
           <b/>
        </k>
    </dd>
    <dd>
        <b/>
        <b/>
    </dd>
</xml_file>

What would be the best way to do this using xslt 2.0?

Thanks a lot for your help.

Edit:

Sorry. I didnt really explain it well..

Iam given a list of elements thats a are organized by their order in the list.

All b's and k's after a dd are supposed to be children of the preceding dd.

All b's after a k are supposed to be children of the preceding k.


Solution

  • You can use

    <xsl:template match="xml_file">
        <xsl:copy>
            <xsl:for-each-group select="*" group-starting-with="dd">
                <xsl:copy>
                    <xsl:for-each-group select="current-group() except ." group-starting-with="k">
                        <xsl:choose>
                            <xsl:when test="self::k">
                                <xsl:copy>
                                    <xsl:apply-templates select="current-group() except ."/>
                                </xsl:copy>
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:apply-templates select="current-group()"/>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:for-each-group>
                </xsl:copy>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>
    

    plus the identity transformation template.