I am new to XSLT and changing it manually will take a lot of time.
<GroupData ID="xxx" Key="4" Temp="yyy">
<ItemData ID="zzz" Value="3"/>
</GroupData>
<GroupData ID="xxx" Key="4" Temp="yyy">
<ItemData ID="www" Value="1982"/>
</GroupData>
I want to have the childs of these multiple GroupData nodes within the same group, i.e.,
<GroupData ID="xxx" Key="4" Temp="yyy">
<ItemData ID="zzz" Value="3"/>
<ItemData ID="www" Value="1982"/>
</GroupData>
So I need to merge/combine/match them on both GroupData's ID and Key attributes (these vary within the file). Also some do not have a Key attribute. How can I do that? I read some other threads (for example, in C# but I don't have that at my disposal) and I checked W3 schools but these are very basic examples. I am using the latest XML Tools 2.3.2 r908 unicode (beta4) for Notepad++ to apply possible transformations (do not know whether it supports XSLT2.0 or XSLT1.0).
Edit: After trying the suggestions below and various things I am stuck since it has multiple levels and possiblly does not have unique IDs:
...
If it is XSLT 2.0 then you can use a nested <xsl:for-each-group>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Groups>
<xsl:for-each-group select="/Groups/GroupData" group-by="@ID">
<xsl:for-each-group select="current-group()" group-by="if(@Key) then @Key else 'no key'">
<GroupData>
<!-- Copy attributes off the *first* GroupData element in the group -->
<xsl:copy-of select="current-group()[1]/@*"/>
<!-- Copy ItemData children from *all* GroupData elements in the group -->
<xsl:copy-of select="current-group()/ItemData" />
</GroupData>
</xsl:for-each-group>
</xsl:for-each-group>
</Groups>
</xsl:template>
</xsl:stylesheet>
(I'm assuming your input file has a root element <Groups>
and uses no namespaces).
If it's XSLT 1.0 then you need to use Muenchian Grouping:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="group-data" match="GroupData" use="concat(@ID, '___', @Key)" />
<xsl:template match="/">
<Groups>
<!--
Iterate over a node set containing just one GroupData element for
each combination of ID and Key
-->
<xsl:for-each select="/Groups/GroupData[count( . | key('group-data', concat(@ID, '___', @Key))[1]) = 1]">
<GroupData>
<!-- Copy attributes from the "prototype" GroupData -->
<xsl:copy-of select="@*"/>
<!--
Copy ItemData children from *all* GroupData elements with matching
ID/Key
-->
<xsl:copy-of select="key('group-data', concat(@ID, '___', @Key))/ItemData" />
</GroupData>
</xsl:for-each>
</Groups>
</xsl:template>
</xsl:stylesheet>
Here I'm doing a single grouping pass based on both the ID and Key attributes by creating a synthetic key
value of {ID}___{Key}
.
This XSLT 1.0 transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kGDByIdKey" match="GroupData"
use="concat(@ID, '+', @Key)"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"GroupData
[generate-id()
=
generate-id(key('kGDByIdKey', concat(@ID, '+', @Key))[1])
]">
<xsl:copy>
<xsl:apply-templates select=
"@*|key('kGDByIdKey', concat(@ID, '+', @Key))/node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="GroupData"/>
</xsl:stylesheet>
when applied on this XML document:
<t>
<GroupData ID="xxx" Key="4" Temp="yyy">
<ItemData ID="zzz" Value="3"/>
</GroupData>
<GroupData ID="yyy" Key="4" Temp="yyy">
<ItemData ID="abc" Value="3"/>
</GroupData>
<GroupData ID="zzz" Temp="yyy">
<ItemData ID="pqr" Value="1982"/>
</GroupData>
<GroupData ID="xxx" Key="4" Temp="yyy">
<ItemData ID="www" Value="1982"/>
</GroupData>
<GroupData ID="yyy" Key="4" Temp="yyy">
<ItemData ID="def" Value="1982"/>
</GroupData>
<GroupData ID="zzz" Temp="yyy">
<ItemData ID="tuv" Value="1982"/>
</GroupData>
</t>
produces the wanted, correct result:
<t>
<GroupData ID="xxx" Key="4" Temp="yyy">
<ItemData ID="zzz" Value="3"/>
<ItemData ID="www" Value="1982"/>
</GroupData>
<GroupData ID="yyy" Key="4" Temp="yyy">
<ItemData ID="abc" Value="3"/>
<ItemData ID="def" Value="1982"/>
</GroupData>
<GroupData ID="zzz" Temp="yyy">
<ItemData ID="pqr" Value="1982"/>
<ItemData ID="tuv" Value="1982"/>
</GroupData>
</t>
Explanation:
Proper use of the Muenchian grouping method and the identity rule.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With