Using the xslt key to find unique values
<ROOT> <AA Aattr="xyz1"> <BB bAttr1="firefox" bAttr2="aaa" > </BB> <BB bAttr1="chrome" bAttr2="aaa" > </BB> <BB bAttr1="firefox" bAttr2="bbb" > </BB> <BB bAttr1="chrome" bAttr2="bbb" > </BB> </AA> <AA Aattr="xyz2"> <BB bAttr1="firefox" bAttr2="aaa" > </BB> <BB bAttr1="chrome" bAttr2="ccc" > </BB> <BB bAttr1="firefox" bAttr2="ddd" > </BB> </AA> I want to select the various \ unique attitute 'bAttr2' values ββin node 'BB', from node 'AA', where the attribute 'Aattr' is xyz1
let's say for this xml, I need to output as "aaa", "bbb"
I tried the following logic with a key. But it didnβt work. Please, help
<xsl:key name="nameDistinct" match="BB" use="@bAttr1"/> <xsl:template match="/"> <xsl:for-each select="ROOT/AA[@Aattr='xyz1']"> <xsl:for-each select="BB[generate-id()=generate-id(key('nameDistinct',@bAttr2)[1])]"> <xsl:value-of select="@bAttr2"/> </xsl:for-each> </xsl:for-each> </xsl:template> You have two options:
Filter out the elements available for the key when you define it:
<xsl:key name="nameDistinct" match="AA[@Aattr = 'xyz1']/BB" use="@bAttr2"/> <xsl:template match="/"> <xsl:for-each select="ROOT/AA/BB[generate-id() = generate-id(key('nameDistinct', @bAttr2)[1])]"> <xsl:value-of select="@bAttr2"/> </xsl:for-each> </xsl:template> or filter inside a grouping expression:
<xsl:key name="nameDistinct" match="BB" use="@bAttr2"/> <xsl:template match="/"> <xsl:for-each select="ROOT/AA/BB[generate-id() = generate-id(key('nameDistinct', @bAttr2) [../@Aattr = 'xyz1'] [1])]"> <xsl:value-of select="@bAttr2"/> </xsl:for-each> </xsl:template> the former approach is slightly less dirty and slightly more efficient, while the latter allows you to parameterize the grouping (ie the group by values ββthat are not hardcoded as "xyz1"), for example:
<xsl:key name="nameDistinct" match="BB" use="@bAttr2"/> <xsl:template match="/"> <xsl:for-each select="ROOT/AA"> <xsl:for-each select="BB[generate-id() = generate-id(key('nameDistinct', @bAttr2) [../@Aattr = current()/@Aattr] [1])]"> <xsl:value-of select="@bAttr2"/> </xsl:for-each> </xsl:for-each> </xsl:template> 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:key name="kCompoundAttribs" match="BB" use="concat(generate-id(..), '+', @bAttr2)"/> <xsl:template match="/"> <xsl:copy-of select= "/*/*[1]/* [generate-id() = generate-id(key('kCompoundAttribs', concat(generate-id(..),'+', @bAttr2) )[1] ) ]"/> </xsl:template> </xsl:stylesheet> when applied to the provided XML document:
<ROOT> <AA Aattr="xyz1"> <BB bAttr1="firefox" bAttr2="aaa" ></BB> <BB bAttr1="chrome" bAttr2="aaa" ></BB> <BB bAttr1="firefox" bAttr2="bbb" ></BB> <BB bAttr1="chrome" bAttr2="bbb" ></BB> </AA> <AA Aattr="xyz2"> <BB bAttr1="firefox" bAttr2="aaa" ></BB> <BB bAttr1="chrome" bAttr2="ccc" ></BB> <BB bAttr1="firefox" bAttr2="ddd" ></BB> </AA> </ROOT> creates two BB elements whose bAttr2 attributes have the desired set of different values (if you only need the string values ββof the attributes, just use xsl:apply-templates or xsl:for-each for this expression):
<BB bAttr1="firefox" bAttr2="aaa"/> <BB bAttr1="firefox" bAttr2="bbb"/> Please note :
This solution is simpler and more understandable and more efficient than "filtering" :)