Reputation: 41
I need to flatten a hierarchical XML structure. Now this probably sounds like a well weathered topic and there are many many links I have poured over. But they are mostly advice on creating hierarchy from a flat structure.
Input XML -
<bom>
<part>
<name>a</name>
<othernodes>abc</othernodes>
<part>
<name>b</name>
<othernodes>abc</othernodes>
<part>
<name>e</name>
<othernodes>abc</othernodes>
</part>
<part>
<name>f</name>
<othernodes>abc</othernodes>
</part>
</part>
<part>
<name>c</name>
<othernodes>abc</othernodes>
<part>
<name>g</name>
<othernodes>abc</othernodes>
</part>
</part>
<part>
<name>d</name>
<othernodes>abc</othernodes>
</part>
</part>
</bom>
Output sought -
<bom>
<part>
<parent/>
<name>a</name>
<othernodes>abc</othernodes>
</part>
<part>
<parent>a</parent>
<name>b</name>
<othernodes>abc</othernodes>
</part>
<part>
<parent>a</parent>
<name>c</name>
<othernodes>abc</othernodes>
</part>
<part>
<parent>a</parent>
<name>d</name>
<othernodes>abc</othernodes>
</part>
<part>
<parent>b</parent>
<name>e</name>
<othernodes>abc</othernodes>
</part>
<part>
<parent>b</parent>
<name>f</name>
<othernodes>abc</othernodes>
</part>
<part>
<parent>c</parent>
<name>g</name>
<othernodes>abc</othernodes>
</part>
</bom>
At first instance I thought this is probably not possible. But then I tried to come up with some sort of xslt. I know recursion here is the key but not sure how to implement. Here's the XSLT I have so far (obviously it does not produce the desired result).
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="bom">
<xsl:element name="bom">
<xsl:apply-templates select="@* | node() [not(child::part)]"/>
<xsl:apply-templates select="part"/>
<!--<xsl:apply-templates select="part/child::part"/>-->
</xsl:element>
</xsl:template>
<xsl:template match="part" >
<xsl:element name="part">
<xsl:element name="parent">
<xsl:value-of select="../part/name"/>
</xsl:element>
<xsl:apply-templates select="@* | node() [not(part)]" />
<xsl:apply-templates select="child::part"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Appreciate if anyone can provide me with some directions on how to approach and solve this problem. Thanks!
Upvotes: 0
Views: 476
Reputation: 7905
You're almost there, your stylesheet just need some fixtures (and there's no need for recursion here):
<xsl:apply-templates select="@* | node() [not(child::part)]"/>
should use the self::
axis instead, to work properly.template match="part"
must be outside the <xsl:element>
<xsl:value-of select="../name"/>
instead of <xsl:value-of select="../part/name"/>
This is the corrected version of your stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="bom">
<xsl:element name="bom">
<xsl:apply-templates select="@* | node()"/>
<!--<xsl:apply-templates select="part/child::part"/>-->
</xsl:element>
</xsl:template>
<xsl:template match="part" >
<xsl:element name="part">
<xsl:apply-templates select="@*"/>
<xsl:element name="parent">
<xsl:value-of select="../name"/>
</xsl:element>
<xsl:apply-templates select="node() [not(self::part)]" />
</xsl:element>
<xsl:apply-templates select="child::part"/>
</xsl:template>
</xsl:stylesheet>
This is the result I obtain, the main difference with what you provided in your resides in that the elements are not ordered exactely the same way:
<?xml version="1.0" encoding="UTF-8"?>
<bom>
<part>
<parent/>
<name>a</name>
<othernodes>abc</othernodes>
</part>
<part>
<parent>a</parent>
<name>b</name>
<othernodes>abc</othernodes>
</part>
<part>
<parent>b</parent>
<name>e</name>
<othernodes>abc</othernodes>
</part>
<part>
<parent>b</parent>
<name>f</name>
<othernodes>abc</othernodes>
</part>
<part>
<parent>a</parent>
<name>c</name>
<othernodes>abc</othernodes>
</part>
<part>
<parent>c</parent>
<name>g</name>
<othernodes>abc</othernodes>
</part>
<part>
<parent>a</parent>
<name>d</name>
<othernodes>abc</othernodes>
</part>
</bom>
Upvotes: 1