How to find element that matches xslt attribute value for each loop
Suppose I have the following XML:
<data>
<foos>
<foo id="1" checked="yes" />
<foo id="2" />
<foo id="3" />
<foo id="4" checked="yes" />
</foos>
<bars>
<bar for="1" name="blub" />
<bar for="2" name="bla" />
<bar for="3" name="baz" />
<bar for="4" name="plim" />
</bars>
</data>
Now I want to print all the attributes of name
this element bar
that point to the element foo
that has the attribute checked
. So for the example above, my xslt outputs blub
and plim
.
Here's what I've tried so far to check if I can print the attribute of an id
element foo
that belongs to each bar
:
<xsl:template match="/">
<xsl:for-each select="//bars/bar">
<xsl:value-of select="../../foos/foo[@id=./@for]/@id" />
</xsl:for-each>
</xsl:template>
but to no avail. I think the problem is that the check foo[@id=./@for]
will select both from @id
and @for
from the element foo
. So how can I tell I want an attribute @for
from my current element in the for loop, but @id
from another current element?
source to share
how can I tell that I want an attribute
@for
from my current element in the for loop, but@id
from another current element?
Use the functioncurrent()
:
<xsl:value-of select="../../foos/foo[@id=current()/@for]/@id" />
In square brackets .
, this is the node that the predicate is testing and current()
is the node that is the current target for-each
.
source to share
Alternatively, which should also improve performance, you can define the key
<xsl:key name="foo-by-id" match="foos/foo" use="@id"/>
and then replace
<xsl:for-each select="//bars/bar">
<xsl:value-of select="../../foos/foo[@id=./@for]/@id" />
</xsl:for-each>
from
<xsl:for-each select="//bars/bar">
<xsl:value-of select="key('foo-by-id', @for)/@id" />
</xsl:for-each>
source to share
To use the idea xsl:key
from @ martin-honnen enter a key for the checked items:
<xsl:key name="checked" match="foos/foo[@checked = 'yes']" use="@id" />
Then your XSLT might become:
<xsl:template match="/">
<xsl:apply-templates select="/data/bars/bar[key('checked', @for)]" />
</xsl:template>
<xsl:template match="bar">
<xsl:value-of select="@name" />
</xsl:template>
Only foo
s checked="yes"
are included in the key checked
(although if you are working with HTML it is more likely that it is checked="checked"
). key('checked', @for)
for an element without a matching checked="yes"
will return an empty sequence of node-set / empty (depending on your version of XSLT), so for those elements the predicate will evaluate as false
, so xsl:apply-templates
only selects the elements you are interested in, so the template for bar
becomes very simple.
source to share