Martin Purdy
Martin Purdy

Reputation: 23

XSLT to transform a flat structure to a multi level structure

I have an XML that looks like this:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Report>
    <BCOT>
        <Detail_Collection>
            <Detail>
                <ElementId>Element1</ElementId>
                <ParentElementId>Element0</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element2</ElementId>
                <ParentElementId>Element1</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element3</ElementId>
                <ParentElementId>Element2</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element4</ElementId>
                <ParentElementId>Element3</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element5</ElementId>
                <ParentElementId>Element3</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element6</ElementId>
                <ParentElementId>Element1</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element7</ElementId>
                <ParentElementId>Element6</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element8</ElementId>
                <ParentElementId>Element7</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element9</ElementId>
                <ParentElementId>Element8</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element10</ElementId>
                <ParentElementId>Element9</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element11</ElementId>
                <ParentElementId>Element10</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element12</ElementId>
                <ParentElementId>Element11</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element13</ElementId>
                <ParentElementId>Element11</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element14</ElementId>
                <ParentElementId>Element11</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element15</ElementId>
                <ParentElementId>Element9</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element16</ElementId>
                <ParentElementId>Element15</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element17</ElementId>
                <ParentElementId>Element16</ParentElementId>
            </Detail>
            <Detail>
                <ElementId>Element18</ElementId>
                <ParentElementId>Element16</ParentElementId>
            </Detail>
        </Detail_Collection>
    </BOMConsistOfTmp>
</Report>

And I need a stylesheet to get it to look like this:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Report>
    <BCOT>
        <Detail_Collection>
            <Detail>
                <ElementId>Element1</ElementId>
                <ParentElementId>Element0</ParentElementId>
                <Detail>
                    <ElementId>Element2</ElementId>
                    <ParentElementId>Element1</ParentElementId>
                    <Detail>
                        <ElementId>Element3</ElementId>
                        <ParentElementId>Element2</ParentElementId>
                        <Detail>
                            <ElementId>Element4</ElementId>
                            <ParentElementId>Element3</ParentElementId>
                        </Detail>
                        <Detail>
                            <ElementId>Element5</ElementId>
                            <ParentElementId>Element3</ParentElementId>
                        </Detail>
                    </Detail>
                </Detail>
                <Detail>
                    <ElementId>Element6</ElementId>
                    <ParentElementId>Element1</ParentElementId>
                    <Detail>
                        <ElementId>Element7</ElementId>
                        <ParentElementId>Element6</ParentElementId>
                        <Detail>
                            <ElementId>Element8</ElementId>
                            <ParentElementId>Element7</ParentElementId>
                            <Detail>
                                <ElementId>Element9</ElementId>
                                <ParentElementId>Element8</ParentElementId>
                                <Detail>
                                    <ElementId>Element10</ElementId>
                                    <ParentElementId>Element9</ParentElementId>
                                    <Detail>
                                        <ElementId>Element11</ElementId>
                                        <ParentElementId>Element10</ParentElementId>
                                        <Detail>
                                            <ElementId>Element12</ElementId>
                                            <ParentElementId>Element11</ParentElementId>
                                        </Detail>
                                        <Detail>
                                            <ElementId>Element13</ElementId>
                                            <ParentElementId>Element11</ParentElementId>
                                        </Detail>
                                        <Detail>
                                            <ElementId>Element14</ElementId>
                                            <ParentElementId>Element11</ParentElementId>
                                        </Detail>
                                    </Detail>
                                </Detail>
                                <Detail>
                                    <ElementId>Element15</ElementId>
                                    <ParentElementId>Element9</ParentElementId>
                                    <Detail>
                                        <ElementId>Element16</ElementId>
                                        <ParentElementId>Element15</ParentElementId>
                                        <Detail>
                                            <ElementId>Element17</ElementId>
                                            <ParentElementId>Element16</ParentElementId>
                                        </Detail>
                                        <Detail>
                                            <ElementId>Element18</ElementId>
                                            <ParentElementId>Element16</ParentElementId>
                                        </Detail>
                                    </Detail>
                                </Detail>
                            </Detail>
                        </Detail>
                    </Detail>
                </Detail>
            </Detail>
        </Detail_Collection>
    </BOMConsistOfTmp>
</Report>

I have read that it is better to use templates when I have a recursive task, but I have never fully understood templates as opposed to for-each.

I have tried using a strategy, where I run it a fixed number of times, but I would really like it to work recursively.

Can someone help me?

Upvotes: 0

Views: 34

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 116982

This is easy to do using a key to identify each detail's children (and also the existence of a parent):

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="parent" match="Detail" use="ElementId" />
<xsl:key name="child" match="Detail" use="ParentElementId" />

<xsl:template match="/Report">
    <Report>
        <BCOT>
            <Detail_Collection>
                <xsl:apply-templates select="BCOT/Detail_Collection/Detail[not(key('parent', ParentElementId))]"/>
            </Detail_Collection>
        </BCOT>
    </Report>
</xsl:template>

<xsl:template match="Detail">
    <xsl:copy>
        <xsl:copy-of select="*"/>
        <xsl:apply-templates select="key('child', ElementId)"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet> 

Upvotes: 1

Related Questions