I'm trying to select all nodes that 1) come after a node with a particular property and 2) have a particular property themselves. So if I had the following XML:
<node id="1"><child attr="valueOfInterest"/></node>
<node id="2"><child attr="boringValue"/></node>
...
<node id="3"><child attr="valueOfInterest"/></node>
<node id="4"><child attr="boringValue"/></node>
<node id="5"><child attr="boringValue"/></node>
<node id="6"><child attr="boringValue"/></node>
...
My XSLT traverses through each node
tag. At each node
, I want it to select all previous node
s that occurred since the most recent node
that had a child
whose attr
was valueOfInterest
. So if I were at node #2, I would want an empty node set. If I were at node #6, I would want to select node #'s 4 and 5. I currently have the following XSLT:
<xsl:variable name="prev_vals"
select="preceding-sibling::node/child[@attr = $someValueICareAbout]/@attr"/>
So this XSLT gets all preceding attr
values that are a particular value. How do I only get those preceding attr
values that are in node
s that come after the most recent node
whose child
has a particular attr
value (i.e., "valueOfInterest")? The id
attribute on node
tags is not guaranteed to be increasing, so we can't compare against that.
Edit: I thought these might be of use:
<xsl:variable name="prev_children_of_interest"
select="preceding-sibling::node/child[@attr != $someValueICareAbout]"/>
<xsl:variable name="mru_child_of_interest"
select="$prev_children_of_interest[count($prev_children_of_interest)]"/>
So that's all previous child
tags with attr=valueOfInterest
and then the most recently used (closest to current node) child
tag that has the attribute I'm looking for. From mru_child_of_interest
we can find the most recently used parent
tag, but how do we then look for nodes that come after that tag?
I am not sure if I understand your question correctly, but here is some XSL 1.0 (additional each-nodes
attributes are informational only):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="nodes">
<xsl:copy>
<xsl:apply-templates select="node"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node">
<xsl:variable name="someValueICareAbout">valueOfInterest</xsl:variable>
<xsl:variable name="recentParticularNode"
select="preceding-sibling::node[child/@attr = $someValueICareAbout][1]"/>
<xsl:variable name="recentParticularNodePosition"
select="count($recentParticularNode/preceding-sibling::node) + 1"/>
<xsl:variable name="currentPosition" select="position()"/>
<xsl:if test="child/@attr != $someValueICareAbout">
<each-nodes id="{@id}" cp="{$currentPosition}"
rpnp="{$recentParticularNodePosition}">
<xsl:copy-of
select="../node[position() > $recentParticularNodePosition
and position() < $currentPosition]"/>
</each-nodes>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<nodes>
<node id="1"><child attr="valueOfInterest"/></node>
<node id="2"><child attr="boringValue2"/></node>
<node id="3"><child attr="valueOfInterest"/></node>
<node id="4"><child attr="boringValue4"/></node>
<node id="5"><child attr="boringValue5"/></node>
<node id="6"><child attr="boringValue6"/></node>
</nodes>
Result XML:
<?xml version="1.0" encoding="UTF-8"?>
<nodes>
<each-nodes id="2" cp="2" rpnp="1"/>
<each-nodes id="4" cp="4" rpnp="3"/>
<each-nodes id="5" cp="5" rpnp="3">
<node id="4">
<child attr="boringValue4"/>
</node>
</each-nodes>
<each-nodes id="6" cp="6" rpnp="3">
<node id="4">
<child attr="boringValue4"/>
</node>
<node id="5">
<child attr="boringValue5"/>
</node>
</each-nodes>
</nodes>
This transformation copies exactly the wanted nodes:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match=
"node[not(child/attr='valueOfInterest')]">
<xsl:variable name="vFollowing" select=
"preceding-sibling::node
[child/@attr='valueOfInterest'][1]
/following-sibling::node"/>
<xsl:variable name="vPreceding" select=
"preceding-sibling::node"/>
<xsl:copy-of select=
"$vFollowing[count(. | $vPreceding)
=
count($vPreceding)
]
"/>
======================
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on this XML document (based on the provided XML fragment and wrapping it in a top element to make it a well-formed XML document):
<t>
<node id="1">
<child attr="valueOfInterest"/>
</node>
<node id="2">
<child attr="boringValue"/>
</node>...
<node id="3">
<child attr="valueOfInterest"/>
</node>
<node id="4">
<child attr="boringValue"/>
</node>
<node id="5">
<child attr="boringValue"/>
</node>
<node id="6">
<child attr="boringValue"/>
</node>...
</t>
the wanted, correct result is produced:
======================
======================
<node id="2">
<child attr="boringValue"/>
</node>
======================
======================
<node id="4">
<child attr="boringValue"/>
</node>
======================
<node id="4">
<child attr="boringValue"/>
</node>
<node id="5">
<child attr="boringValue"/>
</node>
======================
Explanation:
Here we use the well-known Kayessian formula (discovered by the SO user @Michael Kay) for the intersection of two node-sets $ns1
and $ns2
:
ns1[count(.|$ns2) = count($ns2)]
We simply substitute $vFollowing
and $vPreceding
for $ns1
and $ns2
in the above formula.
$vFollowing is defined to contain exactly all the following sibling elements named
nodeof the nearest
node` that satisfies the condition (to be interesting).
$vPreceding
is the set of all node
elements that are preceding siblings of the current (matched) node.
.3. Their intersection is exactly the wanted node-set.
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