My starting XML consists of an unordered flat list of catalogues containing an element that each has their own ID, and a child ID, if they have one.
My starting XML looks like this:
<root>
<catalogue>
<item>
<id>item2</id>
<child>
<id>item3</id>
</child>
</item>
</catalogue>
<catalogue>
<item>
<id>item1</id>
<child>
<id>item2</id>
</child>
</item>
</catalogue>
<catalogue>
<item>
<id>item3</id>
</item>
</catalogue>
</root>
And I need to convert it to a nested tree hierarchy, where the corresponding child item is written inside of its parent item.
My resulting XML should look like this:
<root>
<catalogue>
<item>
<id>item1</id>
<child>
<id>item2</id>
</child>
</item>
<catalogue>
<item>
<id>item2</id>
<child>
<id>item3</id>
</child>
</item>
<catalogue>
<item>
<id>item3</id>
</item>
</catalogue>
</catalogue>
</catalogue>
</root>
I've gotten somewhat close to getting the solution, but I can't get the resulting XML file to not contain unnecessary, duplicate elements.
In the following XML file, the top and bottom catalogues inside the root are duplicate unwanted entries. I need only the large, middle catalogue.
My current result XML looks like this:
<root>
<catalogue>
<item>
<id>item2</id>
<child>
<id>item3</id>
</child>
</item>
<catalogue>
<item>
<id>item3</id>
</item>
</catalogue>
</catalogue>
<catalogue>
<item>
<id>item1</id>
<child>
<id>item2</id>
</child>
</item>
<catalogue>
<item>
<id>item2</id>
<child>
<id>item3</id>
</child>
</item>
<catalogue>
<item>
<id>item3</id>
</item>
</catalogue>
</catalogue>
</catalogue>
<catalogue>
<item>
<id>item3</id>
</item>
</catalogue>
</root>
My current result XML is being created with this XSLT file:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="catalogue">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
<xsl:for-each select="/root/catalogue[item/id/text() = current()/item/child/id/text()]">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I've been trying to find a solution to this for far too long, so I hope someone can help me.
CodePudding user response:
Maybe like this:
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="cat" match="catalogue" use="item/id" />
<xsl:template match="/root">
<xsl:copy>
<!-- start with items that are not children of any item -->
<xsl:apply-templates select="catalogue[not(item/id = //child/id)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="catalogue">
<xsl:copy>
<xsl:copy-of select="item"/>
<!-- recurse with current item's children -->
<xsl:apply-templates select="key('cat', item/child/id)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>