XPath: limit scope of result set
Given the XML
<a>
<c>
<b id="1" value="noob"/>
</c>
<b id="2" value="tube"/>
<a>
<c>
<b id="3" value="foo"/>
</c>
<b id="4" value="goo"/>
<b id="5" value="noob"/>
<a>
<b id="6" value="near"/>
开发者_如何转开发 <b id="7" value="bar"/>
</a>
</a>
</a>
and the Xpath 1.0 query
//b[@id=2]/ancestor::a[1]//b[@value="noob"]
The Xpath above returns both node ids 1 and 5. The goal is to limit the result to just node id=1 since it is the only @value="noob"
element that is a descendant of the same <a>
that (//b[@id=2]
) is also a descendant of.
In other words, "Find all b elements who's value is "noob" that are descendants of the a element which also has a descendant whose id is 2, but is not the descendant of any other a element". How's that for convoluted? In practice the id number and values would be variable and there would hundreds of node types.
If the id=2, we would expect to return element id=1 not id=5 since it is contained in another a element. If the id=4, we would expect to return id=5, but not id=1 since it is not in the first ancestor a element as id=4.
Edit: Based on the comments of Dimitre and Alejandro, I found this helpful blog entry explaining the use of count() with the | union operator as well as some other excellent tips.
Use:
//b[@value='noob']
[count(ancestor::a[1] | //b[@id=2]/ancestor::a[1]) = 1]
Explanation:
The second predicate assures that both b
elements have the same nearest ancestor a
.
Remember: In XPath 1.0 the test for node identity is:
count($n1 | $n2) = 1
First, this
is there some way to limit the result set to the
<b>
elements that are ONLY the children of the immediate<a>
element of the start node (//b[@id=2]
)?
//b[@value='noob'][ancestor::a[1]/b/@id=2]
It's not the same as:
Starting at a node whose id is equal to 2, find all the elements whose value is "noob" that are descendants of the immediate parent c element without passing through another c element
Wich is:
//c[b/@id=2]//*[.='noob'][ancestor::c[1][b/@id=2]]
Besides these expressions, when you are dealing with "context marks" you can use the set's membership test as in:
$node[count(.|$node-set)=count($node-set)]
I leave you its use for this case as an exercise...
//b[@id=2]/ancestor::a[1]//b[@value="noob" and not(ancestor::a[2]=//b[@id=2]/ancestor::a[1])]
?
that works only for your case though, not sure how generic it should be!
精彩评论