I am a beginner and I am trying to group an XML input based on similar category, using XSLT 1.0 Here is the input xml which contains category and location. The out put must group all elements with the same category and list unique locations:
<?xml version="1.0" ?>
<Data>
<Row>
<id>123</id>
<location>/example/games/data.php</location>
<category>gamedata</category>
</Row>
<Row>
<id>456</id>
<location>/example/games/data.php</location>
<category>gamedata</category>
</Row>
<Row>
<id>789</id>
<location>/example/games/score.php</location>
<category>gamedata</category>
</Row>
<Row>
<id>888</id>
<location>/example/games/title.php</location>
<category>gametitle</category>
</Row>
<Row>
<id>777</id>
<location>/example/games/title.php</location>
<category>gametitle</category>
</Row>
<Row>
<id>999</id>
<location>/example/score/title.php</location>
<category>gametitle</category>
</Row>
</Data>
Looking for output as(list only unique location grouped by category):
<project>
<item>
<data>
<category>gamedata</category>
<id>456</id>
<id>789</id>
<id>123</id>
<location>/example/games/data.php</location>
<location>/example/games/score.php</location>
</data>
<data> <category>gametitle</category>
<id>888</id>
<id>777</id>
<id>999</id>
<location>/example/games/title.php</location>
<location>/example/score/title.php</location>
</data>
</item></project>
What I have tried so far:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="keyCategory" match="Row" use="category"/>
<xsl:template match="/">
<project xmlns="xyz.com">
<item >
<name lang="en">Example</name>
<xsl:for-each select="//Row[generate-id(.) = generate-id(key('keyCategory', category)[1])]">
<xsl:for-each select="key('keyCategory', category)">
<data>
<category><xsl:value-of select="category"/></category>
<id><xsl:value-of select="id"/></id>
<location><xsl:value-of select="location"/></location></data>
</xsl:for-each>
</xsl:for-each>
</item>
</project>
What I am actually getting:
<project>
<item>
<data>
<category>gamedata</category>
<id>456</id>
<location>/example/games/data.php</location>
</data>
<data>
<category>gamedata</category>
<id>789</id>
<location>/example/games/score.php</location>
</data>
<data>
<category>gamedata</category>
<id>789</id>
<location>/example/games/score.php</location>
</data>
<data>
<category>gamedata</category>
<id>123</id>
<location>/example/games/data.php</location>
</data>
<data>
<category>gametitle</category>
<id>888</id>
<location>/example/games/title.php</location>
</data>
<data>
<category>gametitle</category>
<id>777</id>
<location>/example/games/title.php</location>
</data>
<data>
<category>gametitle</category>
<id>999</id>
<location>/example/score/title.php</location>
</data>
</item></project>
For your nested grouping problem I think you want to use a second key:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="keyCategory" match="Row" use="category"/>
<xsl:key name="location" match="Row" use="concat(category, '|', location)"/>
<xsl:template match="/">
<project>
<item>
<name lang="en">Example</name>
<xsl:for-each select="//Row[generate-id(.) = generate-id(key('keyCategory', category)[1])]">
<data>
<xsl:copy-of
select="category"/>
<xsl:copy-of select="key('keyCategory', category)/id"/>
<xsl:copy-of
select="key('keyCategory', category)[generate-id() = generate-id(key('location', concat(category, '|', location))[1])]/location"/>
</data>
</xsl:for-each>
</item>
</project>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/94Acsm4/0
This only shows the grouping and ignores that your output uses a different namespace, to create the selected elements in the new namespace I would transform them:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://example.com/">
<xsl:output indent="yes"/>
<xsl:key name="keyCategory" match="Row" use="category"/>
<xsl:key name="location" match="Row" use="concat(category, '|', location)"/>
<xsl:template match="/">
<project>
<item>
<name lang="en">Example</name>
<xsl:for-each select="//Row[generate-id(.) = generate-id(key('keyCategory', category)[1])]">
<data>
<xsl:apply-templates
select="category"/>
<xsl:apply-templates select="key('keyCategory', category)/id"/>
<xsl:apply-templates
select="key('keyCategory', category)[generate-id() = generate-id(key('location', concat(category, '|', location))[1])]/location"/>
</data>
</xsl:for-each>
</item>
</project>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>