1

I have some frustratingly flat XML that looks like this:

<groups>
    <group id="1" depth="1" />
    <group id="2" depth="2" />
    <group id="3" depth="2" />
    <group id="4" depth="3" />
    <group id="5" depth="2" />
    <group id="6" depth="3" />
</groups>

The maximum value of depth appears to be 5.

Which I'd like to turn into XML that looks like this:

<groups>
    <group id="1" depth="1">
        <group id="2" depth="2">
        </group>
        <group id="3" depth="2">
            <group id="4" depth="3">
            </group>
        </group>
        <group id="5" depth="2">
            <group id="6" depth="3">
            </group>
        </group>
    </group>
</groups>

I think I need to use an xsl:key and then for each parentDepth match the following nodes that have depth = parentDepth + 1 until I reach a node with depth <= parentDepth, but I'm unsure how to implement such a rule.

I suppose another logical approach might be to start from the "bottom" and find the most recent node that has depth = childDepth - 1, but I'm unsure if that can even be achieved in XSLT.

I'm also unsure if this can be done recursively for each depth?

I'm using XSLT 1.0.

ThunderFrame
  • 9,352
  • 2
  • 29
  • 60
  • Possible duplicate of [Flat to Nested structure based on attribute value using XSLT](http://stackoverflow.com/questions/11113161/flat-to-nested-structure-based-on-attribute-value-using-xslt) There is a XSLT 1.0 solution as well! – uL1 Oct 28 '16 at 07:27

1 Answers1

3

I think I need to use an xsl:key and then for each parentDepth match the following nodes that have depth = parentDepth + 1

No, you need to look at it in the opposite direction: let each child find its parent and learn its unique id, so it can be called by it.

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:strip-space elements="*"/>

<xsl:key name="child" match="group" use="generate-id(preceding-sibling::group[@depth=current()/@depth - 1][1])" />

<xsl:template match="/groups">
    <xsl:copy>
        <xsl:apply-templates select="group[@depth=1]"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="group">
     <group id="{@id}" depth="{@depth}">
        <xsl:apply-templates select="key('child', generate-id())"/>
    </group>
</xsl:template>

</xsl:stylesheet>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51