I am trying to use Apache Xalan to group a list of product variants by their productId's. Here is an input sample:
***input_1.xml***
<?xml version="1.0" encoding="utf-8"?>
<root>
<variant>
<productId>1</productId>
<price>100</price>
<stock unit="item">10</stock>
<attributes>
<attribute name="color" value="red" />
</attributes>
</variant>
<variant>
<productId>1</productId>
<price>100</price>
<stock unit="item">8</stock>
<attributes>
<attribute name="color" value="blue" />
</attributes>
</variant>
<variant>
<productId>1</productId>
<price>150</price>
<stock unit="item">12</stock>
<attributes>
<attribute name="color" value="green" />
</attributes>
</variant>
<variant>
<productId>2</productId>
<price>200</price>
<stock unit="item">7</stock>
<attributes>
<attribute name="color" value="purple" />
<attribute name="material" value="faux-leather" />
</attributes>
</variant>
<variant>
<productId>3</productId>
<price>190</price>
<stock unit="item">22</stock>
<attributes>
<attribute name="color" value="yellow" />
<attribute name="size" value="XL" />
</attributes>
</variant>
<variant>
<productId>3</productId>
<price>180</price>
<stock unit="item">13</stock>
<attributes>
<attribute name="color" value="yellow" />
<attribute name="size" value="L" />
</attributes>
</variant>
<variant>
<productId>3</productId>
<price>170</price>
<stock unit="item">5</stock>
<attributes>
<attribute name="color" value="yellow" />
<attribute name="size" value="M" />
</attributes>
</variant>
<variant>
<productId>3</productId>
<price>170</price>
<stock unit="item">7</stock>
<attributes>
<attribute name="color" value="yellow" />
<attribute name="size" value="S" />
</attributes>
</variant>
<variant>
<productId>3</productId>
<price>180</price>
<stock unit="item">12</stock>
<attributes>
<attribute name="color" value="yellow" />
<attribute name="size" value="XS" />
</attributes>
</variant>
</root>
I then use the following command from the shell:
xalan -in input_1.xml -xsl muenchian_1.xsl -out output_1.xml -indent 4
To transform the input with the following stylesheet:
***muenchian_1.xml***
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:strip-space elements="*" />
<xsl:output method="xml" indent="yes"/>
<xsl:key name="variants-by-productId" match="/root/variant" use="productId"/>
<xsl:template match="@*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/root/variant[productId][generate-id() =
generate-id(key('variants-by-productId', productId)[1])]" priority="1">
<product-listing-group>
<productId>
<xsl:value-of select="productId"/>
</productId>
<xsl:for-each select="key('variants-by-productId', productId)">
<xsl:call-template name="identity" />
</xsl:for-each>
</product-listing-group>
</xsl:template>
</xsl:stylesheet>
Expecting to get the following output:
***expected_1.xml***
<?xml version="1.0" encoding="utf-8"?>
<root>
<product>
<productId>1</productId>
<variant>
<price>100</price>
<stock unit="item">10</stock>
<attributes>
<attribute name="color" value="red" />
</attributes>
</variant>
<variant>
<price>100</price>
<stock unit="item">8</stock>
<attributes>
<attribute name="color" value="blue" />
</attributes>
</variant>
<variant>
<price>150</price>
<stock unit="item">12</stock>
<attributes>
<attribute name="color" value="green" />
</attributes>
</variant>
</product>
<product>
<productId>2</productId>
<variant>
<price>200</price>
<stock unit="item">7</stock>
<attributes>
<attribute name="color" value="purple" />
<attribute name="material" value="faux-leather" />
</attributes>
</variant>
</product>
<product>
<productId>3</productId>
<variant>
<price>190</price>
<stock unit="item">22</stock>
<attributes>
<attribute name="color" value="yellow" />
<attribute name="size" value="XL" />
</attributes>
</variant>
<variant>
<price>180</price>
<stock unit="item">13</stock>
<attributes>
<attribute name="color" value="L" />
</attributes>
</variant>
<variant>
<price>170</price>
<stock unit="item">5</stock>
<attributes>
<attribute name="color" value="M" />
</attributes>
</variant>
<variant>
<price>170</price>
<stock unit="item">7</stock>
<attributes>
<attribute name="color" value="S" />
</attributes>
</variant>
<variant>
<price>180</price>
<stock unit="item">12</stock>
<attributes>
<attribute name="color" value="XS" />
</attributes>
</variant>
</product>
</root>
but instead I get:
***output_1.xml***
<?xml version="1.0" encoding="UTF-8"?>
<root>
<product-listing-group>
<productId>1</productId>
<variant>
<productId>1</productId>
<price>100</price>
<stock unit="item">10</stock>
<attributes>
<attribute name="color" value="red"/>
</attributes>
</variant>
<variant>
<productId>1</productId>
<price>100</price>
<stock unit="item">8</stock>
<attributes>
<attribute name="color" value="blue"/>
</attributes>
</variant>
<variant>
<productId>1</productId>
<price>150</price>
<stock unit="item">12</stock>
<attributes>
<attribute name="color" value="green"/>
</attributes>
</variant>
</product-listing-group>
<variant>
<productId>1</productId>
<price>100</price>
<stock unit="item">8</stock>
<attributes>
<attribute name="color" value="blue"/>
</attributes>
</variant>
<variant>
<productId>1</productId>
<price>150</price>
<stock unit="item">12</stock>
<attributes>
<attribute name="color" value="green"/>
</attributes>
</variant>
<product-listing-group>
<productId>2</productId>
<variant>
<productId>2</productId>
<price>200</price>
<stock unit="item">7</stock>
<attributes>
<attribute name="color" value="purple"/>
<attribute name="material" value="faux-leather"/>
</attributes>
</variant>
</product-listing-group>
<product-listing-group>
<productId>3</productId>
<variant>
<productId>3</productId>
<price>190</price>
<stock unit="item">22</stock>
<attributes>
<attribute name="color" value="yellow"/>
<attribute name="size" value="XL"/>
</attributes>
</variant>
<variant>
<productId>3</productId>
<price>180</price>
<stock unit="item">13</stock>
<attributes>
<attribute name="color" value="yellow"/>
<attribute name="size" value="L"/>
</attributes>
</variant>
<variant>
<productId>3</productId>
<price>170</price>
<stock unit="item">5</stock>
<attributes>
<attribute name="color" value="yellow"/>
<attribute name="size" value="M"/>
</attributes>
</variant>
<variant>
<productId>3</productId>
<price>170</price>
<stock unit="item">7</stock>
<attributes>
<attribute name="color" value="yellow"/>
<attribute name="size" value="S"/>
</attributes>
</variant>
<variant>
<productId>3</productId>
<price>180</price>
<stock unit="item">12</stock>
<attributes>
<attribute name="color" value="yellow"/>
<attribute name="size" value="XS"/>
</attributes>
</variant>
</product-listing-group>
<variant>
<productId>3</productId>
<price>180</price>
<stock unit="item">13</stock>
<attributes>
<attribute name="color" value="yellow"/>
<attribute name="size" value="L"/>
</attributes>
</variant>
<variant>
<productId>3</productId>
<price>170</price>
<stock unit="item">5</stock>
<attributes>
<attribute name="color" value="yellow"/>
<attribute name="size" value="M"/>
</attributes>
</variant>
<variant>
<productId>3</productId>
<price>170</price>
<stock unit="item">7</stock>
<attributes>
<attribute name="color" value="yellow"/>
<attribute name="size" value="S"/>
</attributes>
</variant>
<variant>
<productId>3</productId>
<price>180</price>
<stock unit="item">12</stock>
<attributes>
<attribute name="color" value="yellow"/>
<attribute name="size" value="XS"/>
</attributes>
</variant>
</root>
As you can see while the variants are grouped correctly, all the variants except for the first in their respective groups are repeated twice, once withing the grouping and once just outside of it.
Why is this? And how can I fix it?
You need to block processing for the second, third, fourth, .. variant in a group, otherwise the default identity transformation copies them:
<xsl:template match="/root/variant[productId][not(generate-id() = generate-id(key('variants-by-productId', productId)[1]))]"/>