Warm tip: This article is reproduced from serverfault.com, please click

XSLT: How to transform the xml data into a hierarchical structure using XSLT

发布于 2020-11-28 16:46:56

I'm trying to use XSLT to create a hierarchical XML file from a flat XML file, and not sure what the best approach is.

The XML has data elements to be processed, each data elements has "Properties" and "Relations". Properties - contains info related to the current data. Relations - contains info related to the child elements (object) and Parent ID.

If there is no Parent ID for a element, the data has to be grouped to Level 1 and subsequent child elements should be grouped to Level2 and so on... The example i have provided below contains data and the relations till 4 levels. The actual xml contains even more levels. I have tried combination of for-each and choose function and i was able to get up to two levels only. Appreciate if you could help me with the XSLT to transform the xml to obtain the complete levels. Thanks in Advance!

Input XML:

<results>
    <content>
        <data>
            <id>12345</id>
            <properties>
                <name>ABC</name>
                <date>2020-07-18</date>
            </properties>
            <relations>
                <object>
                    <data>
                        <id>67890</id>
                    </data>
                </object>
            </relations>
        </data>

        <data>
            <id>67890</id>
            <properties>
                <name>XYZ</name>
                <date>2020-07-18</date>
            </properties>
            <relations>
                <object>
                    <data>
                        <id>22246</id>
                    </data>
                </object>
                <parent>
                    <data>
                        <id>12345</id>
                    </data>
                </parent>
            </relations>
        </data>

        <data>
            <id>22246</id>
            <properties>
                <name>DEF</name>
                <date>2020-07-18</date>
            </properties>
            <relations>
                <object>
                    <data>
                        <id>68681</id>
                    </data>
                </object>
                <parent>
                    <data>
                        <id>67890</id>
                    </data>
                </parent>
            </relations>
        </data>

        <data>
            <id>68681</id>
            <properties>
                <name>UVW</name>
                <date>2020-07-18</date>
            </properties>
            <relations>
                <parent>
                    <data>
                        <id>22246</id>
                    </data>
                </parent>
            </relations>
        </data>
    <content>
</results>

Expected Output:

<results>
    <content>
        <Level>
            <id>12345</id>
            <properties>
                <name>ABC</name>
                <date>2020-07-18</date>
            </properties>
            <relations>
                <object>
                    <data>
                        <id>67890</id>
                    </data>
                </object>
            </relations>
            
            <Leve2>
                <id>67890</id>
                <properties>
                    <name>XYZ</name>
                    <date>2020-07-18</date>
                </properties>
                <relations>
                    <object>
                        <data>
                            <id>22246</id>
                        </data>
                    </object>
                    <parent>
                        <data>
                            <id>12345</id>
                        </data>
                    </parent>
                </relations>
                
                <Leve3>
                    <id>22246</id>
                    <properties>
                        <name>DEF</name>
                        <date>2020-07-18</date>
                    </properties>
                    <relations>
                        <object>
                            <data>
                                <id>68681</id>
                            </data>
                        </object>
                        <parent>
                            <data>
                                <id>67890</id>
                            </data>
                        </parent>
                    </relations>
                    
                    <Leve4>
                        <id>68681</id>
                        <properties>
                            <name>UVW</name>
                            <date>2020-07-18</date>
                        </properties>
                        <relations>
                            <parent>
                                <data>
                                    <id>22246</id>
                                </data>
                            </parent>
                        </relations>
                    </Leve4>
                </Leve3>
            </Leve2>
        </Level>        
    <content>
</results>

Hierarchy:

<Level1>
    Parent1
    <Level2>
        Child1
        ...
        <Level3>
            Child1a
            ...
            <Level4>
                Child1aa
                ...
            </Level4>
        </Level3>
    </Level2>
<Level1>

<Level1>
    Parent2
    <Level2>
        Child2
        ...
        <Level3>
            Child2a
            ...
            <Level4>
                Child2aa
                ...
            </Level4>
        </Level3>
    </Level2>
<Level>
...
Questioner
Shashank G
Viewed
0
Michael Kay 2020-11-29 04:27:16

You haven't explained the problem very clearly. I'm assuming that you want X to be a child of Y in the output if the <parent> element of X contains a reference to the <id> of Y. (If I'm right that Ithat's the problem, then your data is cluttered with a lot of confusing stuff that's irrelevant to the problem and just distracts from understanding it).

On this assumption, first define a key that makes it easy to find the items with a given parent:

<xsl:key name="parent-key" match="content/data" use="relations/parent/object/id"/>

Then when you're processing one record, just use the key() function to grab its children:

<xsl:template match="content/data">
  <xsl:param name="level" select="1"/>
  <xsl:element name="Level{$level}">
    <xsl:copy-of select="."/>
    <xsl:apply-templates select="key('parent-key', id)">
      <xsl:with-param name="level" select="$level + 1"/>
    </xsl:apply-templates>
  </xsl:element>
</xsl:template>

and then start processing at the highest-level element (in your example this is the first one, I don't know if that's always the case)

<xsl:template match="/">
  <results>
    <content>
      <xsl:apply-templates select="data[1]"/>
    </content>
  </results>
</xsl:template>

Not tested.