I wonder if there´s a way to search an xml for duplicates, then when found to aggregate all the duplicates to one node. F.eks
<car name="one">
<person>john</person>
<person>Jay</person>
</car>
<car name="two">
<person>kim</person>
<person>chris</person>
</car>
<car name="one">
<person>jane</person>
<person>liz</person>
</car>
Should be:
<car name="one">
<person>john</person>
<person>Jay</person>
<person>jane</person>
<person>liz</person>
</car>
<car name="two">
<person>kim</person>
<person>chris</person>
</car>
All help is much appreciated!
Br Kim
In XSLT 1.0 you have to use something called the Muenchian Method which is a process of grouping elements using the key
and generate-id
functions.
The process goes something like this:
You first define a key that represents the data that you want to group with.
<xsl:key name="car-by-name" match="car" use="@name"/>
Then you you use that key in a template match by generating an id based on that key.
<xsl:apply-templates select="car[generate-id() = generate-id(key('car-by-name', @name)[1])]" mode="group"/>
Now that you have your nodes grouped, all you do is use that key again to grab all of the nodes within that key.
<xsl:apply-templates select="key('car-by-name', @name)"/>
So now to show it all together, when you take this XML (added document
to make it well-formed).
<document>
<car name="one">
<person>john</person>
<person>Jay</person>
</car>
<car name="two">
<person>kim</person>
<person>chris</person>
</car>
<car name="one">
<person>jane</person>
<person>liz</person>
</car>
</document>
And apply this XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:key name="car-by-name" match="car" use="@name"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="document">
<xsl:copy>
<xsl:apply-templates select="car[generate-id() = generate-id(key('car-by-name', @name)[1])]" mode="group"/>
</xsl:copy>
</xsl:template>
<xsl:template match="car" mode="group">
<car name="{@name}">
<xsl:apply-templates select="key('car-by-name', @name)"/>
</car>
</xsl:template>
<xsl:template match="car">
<xsl:apply-templates select="@*|node()"/>
</xsl:template>
</xsl:stylesheet>
It produces this result.
<?xml version="1.0" encoding="UTF-8"?>
<document>
<car name="one">
<person>john</person>
<person>Jay</person>
<person>jane</person>
<person>liz</person>
</car>
<car name="two">
<person>kim</person>
<person>chris</person>
</car>
</document>
If you are using XSLT 2.0 you can make use of xsl:for-each-group
as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/data">
<data>
<xsl:for-each-group select="car" group-by="@name">
<car name="{current-grouping-key()}">
<xsl:for-each select="current-group()/person">
<person><xsl:value-of select="." /></person>
</xsl:for-each>
</car>
</xsl:for-each-group>
</data>
</xsl:template>
</xsl:stylesheet>
When applied on the following input:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<car name="one">
<person>john</person>
<person>Jay</person>
</car>
<car name="two">
<person>kim</person>
<person>chris</person>
</car>
<car name="one">
<person>jane</person>
<person>liz</person>
</car>
</data>
It produces the correct ouput:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<car name="one">
<person>john</person>
<person>Jay</person>
<person>jane</person>
<person>liz</person>
</car>
<car name="two">
<person>kim</person>
<person>chris</person>
</car>
</data>