<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 distinct\unique values of attibute 'bAttr2' in node 'BB', from node 'AA' where attribute 'Aattr' is xyz1
say for the given xml, i need to output as "aaa","bbb"
I tried the following logic using key. But didnt worked. 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 here:
Filter the items available to 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 within the 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 a bit less messy and marginally more efficient, while the latter allows you to parameterize the grouping (i.e. group on values that aren't hardcoded as 'xyz1'), e.g.:
<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 transformation:
<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 on 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>
produces two BB
elements, whose bAttr2
attributes have the desired set of distinct values (if you want just the string values of the attributes, just use xsl:apply-templates
or xsl:for-each
for that expression):
<BB bAttr1="firefox" bAttr2="aaa"/>
<BB bAttr1="firefox" bAttr2="bbb"/>
Do note:
This solution is simpler and more easier to understand and more efficient than "filtering" :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With