I. XSLT 1.0 Solution :
This conversion is :
<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="kPersByNameAndAge" match="person" use="concat(name, '+', age)"/> <xsl:key name="kmailByNameAndAge" match="mail" use="concat(../../name, '+', ../../age)"/> <xsl:key name="kmailByNameAndAgeAndVal" match="mail" use="concat(../../name, '+', ../../age, '+', .)"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="/*"> <persons> <xsl:apply-templates select= "person[generate-id() = generate-id(key('kPersByNameAndAge', concat(name, '+', age) ) [1] ) ] "/> </persons> </xsl:template> <xsl:template match="mails"> <mails> <xsl:apply-templates select= "key('kmailByNameAndAge', concat(../name, '+', ../age)) [generate-id() = generate-id(key('kmailByNameAndAgeAndVal', concat(../../name, '+', ../../age, '+', .) ) [1] ) ] "/> </mails> </xsl:template> </xsl:stylesheet>
when applied to the provided XML document :
<persons> <person> <name>Tom</name> <age>20</age> <mails> <mail> x@test.com </mail> <mail> y@test.com </mail> </mails> </person> <person> <name>Tom</name> <age>20</age> <mails> <mail> y@test.com </mail> <mail> z@test.com </mail> </mails> </person> </persons>
creates the desired, correct result :
<persons> <person> <name>Tom</name> <age>20</age> <mails> <mail> x@test.com </mail> <mail> y@test.com </mail> <mail> z@test.com </mail> </mails> </person> </persons>
II. XSLT 2.0 Solution
<xsl:stylesheet version="2.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="kmailByNameAndAge" match="mail" use="concat(../../name, '+', ../../age)"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="/*"> <persons> <xsl:for-each-group select="person" group-by="concat(name, '+', age)"> <xsl:apply-templates select="."/> </xsl:for-each-group> </persons> </xsl:template> <xsl:template match="mails"> <mails> <xsl:for-each-group select= "key('kmailByNameAndAge', concat(../name, '+', ../age))" group-by="concat(../../name, '+', ../../age, '+', .)" > <xsl:apply-templates select="."/> </xsl:for-each-group> </mails> </xsl:template> </xsl:stylesheet>
Dimitre novatchev
source share