ldrg
ldrg

Reputation: 4501

Using XPath to find the first child of a specific grandparent?

I'm dealing with some gnarly HTML. Here's a stylized example of the ancestry of a given element:

/html/body/foo/bar/baz/quux/b

I have a reference to body in my code and b is the context element for my XPath query. In the above example how do I find foo? More descriptively, how do I find the first element in a given ancestry which is the child of another given element? The elements between body and b are unknown and varied in type and depth at runtime. I can do this outside of XPath by iterating over the ancestors from b until I get to body but I'm wondering if there's some XPath ancestor magic to find this relative reference.

Upvotes: 2

Views: 4341

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243479

Use:

ancestor::body[1]/foo

this selects the element that is the first element child of body that is the first ancestor body of the context node.

Even if you know that there are no nested body elements, the above is slightly more efficient than:

ancestor::body/foo

because evaluating this last expression, all ancestors will be tested for being a body.

UPDATE:

In a comment the OP clarified:

Well, I'm trying to find the intersection of the two sets (ancestors of context node, children of body)

In XPath 1.0 this is q direct substitution into the well-known Kayessian formula for node-set intersection:

$ns1[count(.|$ns2) = count($ns2)]

In this case we substitute $ns1 with ancestor::* and $ns2 with /*/body/*:

ancestor::*[count(. | /*/body/*) = count(/*/body/*)]

In XPath 2.0 this is easier, using the intersect operator:

ancestor::* intersect /*/body/*

Upvotes: 2

Norman Gray
Norman Gray

Reputation: 12514

Without testing it, I would guess that ancestor::body/*[1] might do it.

Explanation: ancestor::body is the first body element along the ancestor 'axis', /* selects all of the children of that element, and [1] selects the first element of that list of children, which in this case is foo.

Upvotes: 2

Related Questions