Atıfcan Ergin
Atıfcan Ergin

Reputation: 138

xPath that selects elements between two specific sibling

I have the following xml and I need to select the only b's having type "desc" under the element having type of "header".

<?xml version="1.0" encoding="UTF-8"?>
<x>
<a>
   <b type="header" text="A." />
</a>
<a>
   <b type="desc" text="A1." />
</a>
<a>
   <b type="desc" text="A2." />
</a>
<a>
   <b type="desc" text="A3." />
</a>
<a>
   <b type="desc" text="A4." />
</a>
<a>
   <b type="header" text="B." />
</a>
<a>
   <b type="desc" text="B1." />
</a>

Code should be something like that: /x/a/b/[@type='desc']....

output must be:

A1.
A2.
A3.
A4.

Thanks in advance...

Upvotes: 0

Views: 1383

Answers (3)

ThW
ThW

Reputation: 19482

By default XPath uses the children axis, but in this case you need to check the preceding and following siblings.

First you need to specify the starting a node. It has to contain an b node of the type header and with the text A..

a[b[@type="header" and @text = "A."]]

It has to be a preceding sibling of the nodes you want to fetch.

preceding-sibling::a[b[@type="header" and @text = "A."]]

The second limit is the first following sibling of this node with the type header:

preceding-sibling::a[b[@type="header" and @text = "A."]]/following-sibling::a[b[@type="header"]][1]

Next make it a condition for the a nodes. So you select only nodes between these two:

/x/a[
   preceding-sibling::a[b[@type="header" and @text = "A."]] and 
   preceding-sibling::a[b[@type="header" and @text = "A."]]/following-sibling::a[b[@type="header"]][1]
  ]

Last select the text attribute nodes of b child nodes with the type attribute desc

/x/a[
   preceding-sibling::a[b[@type="header" and @text = "A."]] and 
   preceding-sibling::a[b[@type="header" and @text = "A."]]/following-sibling::a[b[@type="header"]][1]
  ]/b[@type = "desc"]/@text

Upvotes: 1

Mike
Mike

Reputation: 2761

/x/a/b[@type="header"]/../following-sibling::*[0][name()='a']/b[@type="desc"]/@text

Untested but you find the header, go back up to a, take the first following element, check that it's an a, then look for a child of b, type desc and take the text attribute.
......

Upvotes: 1

har07
har07

Reputation: 89285

You can try this way :

/x/a[following-sibling::a[b[@type='header']] and preceding-sibling::a[b[@type='header']]]/b[@type='desc']/@text

Upvotes: 2

Related Questions