tags:

views:

53

answers:

3

I have the following XML (which is actually HTML):

<html>
    <h4>something</h4>
    <p>a</p>
    <p>b</p>
    <h4>otherthing</h4>
    <p>c</p>
</html>

Can a XPath selects the "p" nodes that are following siblings of the first "h4" node but not following siblings of second "h4" node (selecting "p" node a & b only)?

+1  A: 

Assuming no <p> preceding the first <h4>,

//h4[2]/preceding-sibling::p
KennyTM
it's not selecting the first h4's following siblings, but the second h4's preceding siblings, which is a difference, although it yields the correct result
Gordon
@Gordon //h4[1]/following-sibling::p returns a b and c. Kenny's answer makes sense, but would also need to handle the case where there is only one h4 element.
nonnb
@nonnb I didnt say it doesnt make sense. I am just saying, it's different from what was asked.
Gordon
+2  A: 

My take

//p[preceding-sibling::h4[1] and not(preceding-sibling::h4[position() > 1])]

finds all p elements which are siblings of the first h4 but not siblings of any other h4 on the same axis

Alternative

//h4[1]/following-sibling::p[count(preceding-sibling::h4) = 1]

finds all following p element of the first h4 element that do have exactly one preceding h4 element

Gordon
@Gordon: I think you could simplify your first answer into `/*/p[not(preceding-sibling::h4[2])]` and second answer into `/*/p[count(preceding-sibling::h4)=1]`.
Alejandro
+4  A: 

Use:

/*/h4[1]/following-sibling::p
            [not(count(preceding-sibling::* | /*/h4[2])
                =
                 count(preceding-sibling::*)
                 )
             ]

More generally, the intersection of two nodesets $ns1 and $ns2 is selected by:

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

The fact that a node $n is not in a node-set $ns1 is expressed by:

not(count($n | $ns1) = count($ns1))

This is fundamental set theory and usage of the standard XPath | (union operator and not() function.

Dimitre Novatchev
@Dimitre: +1 for set theory invocation.
Alejandro