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>
...
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.
Sorry about that. I have added additional info and formatted the xml. Yes, you are right. "X to be a child of Y in the output if the <parent> element of X contains a reference to the <id> of Y". I need all the nodes with no parent in Level1 and All their chidren in Level2 and all the children of Level2 in Level3 and so on... The final level contains only parent ID in the relations node. I was not aware of the key function, thanks for the guidance on the xslt structure. I will give it a try. Based on the additional info i added, please let me know if i need to consider any other parameter.
It worked. Thank you very much!