This question already has an answer here:
-
combining xml, xpath or xquery
1 answer
I need to map the following but its difficult because it has different names:
<main>
<order>
<ID>123</ID>
<Name>ABC</Name>
</order>
<order>
<ID>4556</ID>
<Name>AAA</Name>
<ParentID>123</ParentID>
</order>
</main>
The result should be:
<main>
<order>
<ID>123</ID>
<Name>ABC</Name>
<order>
<ID>4556</ID>
<Name>AAA</Name>
<ParentID>123</ParentID>
</order>
</order>
</main>
Like @michael.hor257k said, conceptually this question is the same as your previous question. I think you're just not understanding the concept. Hopefully the comments in my example will help.
If I understand the question correctly, you want to nest order
elements inside of their "parent" order
element based on ParentID
. Since we're basing it on the ParentID, that's what we'll use for our key...
XML Input
<main>
<order>
<ID>123</ID>
<Name>ABC</Name>
</order>
<order>
<ID>4556</ID>
<Name>AAA</Name>
<ParentID>123</ParentID>
</order>
</main>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<!--Create a key containing order elements that contain a non-empty ParentID.-->
<xsl:key name="orderByParentId" match="order[string(ParentID)]" use="ParentID"/>
<!--Identity transform (https://www.w3.org/TR/xslt#copying)-->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/main">
<xsl:copy>
<!--To get the high level order elements, only apply-templates when order
does not contain a non-empty ParentID child.-->
<xsl:apply-templates
select="@*|order[not(string(ParentID))]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="order">
<xsl:copy>
<!--Don't apply-templates to ParentID; we don't want to keep those.
Do apply-templates to the key 'orderByParentId' when the key matches
the current order's ID child.-->
<xsl:apply-templates
select="@*|*[not(self::ParentID)]|key('orderByParentId',ID)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output
<main>
<order>
<ID>123</ID>
<Name>ABC</Name>
<order>
<ID>4556</ID>
<Name>AAA</Name>
</order>
</order>
</main>
One approach to copy all order
nodes with index n>1
into the first one is the following XSLT:
<?xml version = "1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="text()" /> <!-- suppresses copying of text() nodes -->
<xsl:template match="main"> <!-- replicates the "main" node -->
<main>
<xsl:apply-templates />
</main>
</xsl:template>
<xsl:template match="order[1]"> <!-- matches the first "order" node and copies all following ones -->
<order>
<xsl:copy-of select="*" />
<xsl:copy-of select="following-sibling::order" />
</order>
</xsl:template>
</xsl:stylesheet>
The output looks like this:
<?xml version="1.0"?>
<main>
<order>
<ID>123</ID>
<Name>ABC</Name>
<order>
<ID>4556</ID>
<Name>AAA</Name>
<ParentID>123</ParentID>
</order>
</order>
</main>