开发者

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!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜