CJ Dennis
CJ Dennis

Reputation: 4346

Select multiple blocks of nodes including the start and end nodes with XPath 1.0

The following is a simplified version of my XML:

<div><p class="start">1</p></div>
<div><p class="data">2</p></div>
<div><p class="data">3</p></div>
<div><p class="end">4</p></div>
<div><p class="data">5</p></div>
<div><p class="start">6</p></div>
<div><p class="data">7</p></div>
<div><p class="end">8</p></div>

This is a simplified version of my code:

<?php
...
$start_nodes = $finder->query('//div[p/@class="start"]');
foreach ($start_nodes as $node) {
  $data_nodes = $finder->query('following-sibling::div[p/@class="end"][1]/preceding-sibling::*', $node);
  ...
}

I don't know how to select the <div><p class="start"/></div> node, the next <div><p class="end"/></div> node and all the nodes in between. In the above example I want to get 1-4, then 6-8 and skip 5.

I'm using XPath 1.0 in PHP and it's not XLST.

Upvotes: 1

Views: 217

Answers (1)

har07
har07

Reputation: 89305

This is one possible way :

$start_nodes = $finder->query('//div[p/@class="start"]');
foreach ($start_nodes as $node) {
    $count = $finder->evaluate('count(preceding-sibling::div[p/@class="start"])', $node)+1;
    echo 'start '. $count .' : <br>';
    $start = 'self::*';
    $end = 'following-sibling::div[p/@class="end"][1]';
    $inbetween = 'following-sibling::div[p/@class="end"][1]/preceding-sibling::*[count(preceding-sibling::div[p/@class="start"])='.$count.']';
    $data_nodes = $finder->query($start.' | '.$inbetween.' | '.$end, $node);
    foreach($data_nodes as $d){
        echo $d->nodeValue .", ";
    }
    echo "<br><br>";
}

$data_nodes in above is the result of union (|) of three separate xpath expressions; first xpath is for selecting 'start element' ($start), the second is for selecting 'end element' ($end), and the last is for selecting elements between start and end elements ($inbetween).

Demo

output :

start 1 :
1, 2, 3, 4,

start 2 :
6, 7, 8,

Upvotes: 1

Related Questions