PHP Xpath - Find Element with a Value and Also Get Elements Before and After Element

PHP xpath - find element with a value and also get elements before and after element

Use this:

$project = $xml_object->xpath("//photo[filename='$image_id']");

XPath how to query the next node

Since League with id = 20 is halfway through the XML file you can do it dynamically using XPath. Here's a simple PHP code snippet:

<?php
$xml = <<<XML
<Leagues>
<League>
<Id>20</Id>
<Name>Major League Soccer</Name>
<Country>USA</Country>
<Historical_Data>Partial</Historical_Data>
<Fixtures>Yes</Fixtures>
<Livescore>Yes</Livescore>
<NumberOfMatches>135</NumberOfMatches>
<LatestMatch>2013-06-16T04:00:00+02:00</LatestMatch>
</League>
<League>
<Id>33</Id>
<Name>Allsvenskan</Name>
<Country>Sweden</Country>
<Historical_Data>Partial</Historical_Data>
<Fixtures>Yes</Fixtures>
<Livescore>Yes</Livescore>
<NumberOfMatches>88</NumberOfMatches>
<LatestMatch>2013-06-15T16:00:00+02:00</LatestMatch>
</League>
</Leagues>
XML;

$sxe = new SimpleXMLElement($xml);

// Retrieve league with Id = 20
$league20 = $sxe->xpath("//League[Id='20']");
print_r($league20);

// Retrieve league right after the one with Id = 20
$leagueAfter20 = $sxe->xpath("//League[Id='20']/following-sibling::League[1]");
print_r($leagueAfter20);

Output

Array
(
[0] => SimpleXMLElement Object
(
[Id] => 20
[Name] => Major League Soccer
[Country] => USA
[Historical_Data] => Partial
[Fixtures] => Yes
[Livescore] => Yes
[NumberOfMatches] => 135
[LatestMatch] => 2013-06-16T04:00:00+02:00
)
)
Array
(
[0] => SimpleXMLElement Object
(
[Id] => 33
[Name] => Allsvenskan
[Country] => Sweden
[Historical_Data] => Partial
[Fixtures] => Yes
[Livescore] => Yes
[NumberOfMatches] => 88
[LatestMatch] => 2013-06-15T16:00:00+02:00
)
)

php - xpath select element and then select children

The second param to the query is a $contextnode that way you can use relative XPath like this:

foreach ($articles as $article) {
$href = $xpath->query('./a/@href', $article)->item(0)->nodeValue;
# $href = $xpath->evaluate('string(./a/@href)', $article); # using evaluate
}

How to select the text() immediately following an element conditionally in XPath?

You can achieve this with the following:

<?php
$xmldoc = new DOMDocument();
$xmldoc->loadXML(<<<XML
<span id="outer">
<div style="color:blue">51</div>
<span class="main">Gill</span>$500
<span style="color:red">11</span>
<span></span>James
<div style="color:red">158</div>
<div class="sub">Mary</div>
</span>
XML
);
$xpath = new Domxpath($xmldoc);

$nodes = $xpath->query("//span[@id='outer']/*");
$str_out = "";
foreach ($nodes as $node)
{
if ($node->hasAttribute('class'))
{
if ($node->getAttribute('class') == "main")
$str_out .= $node->nodeValue . " ";
}

else if ($node->hasAttribute('style'))
{
$node_style = $node->getAttribute('style');
preg_match('~color:(.*)~', $node_style, $temp);
if ($temp[1] == "blue")
$str_out .= $node->nodeValue . " ";
}

// Now evaluate if the IMMEDIATELY next sibling is text()
$next_node = $xpath->query('./following-sibling::node()[1]/self::text()[normalize-space()]', $node);
if ($next_node->length)
{
$str_out .= trim($next_node->item(0)->nodeValue) . " ";
}
}
echo $str_out;

The XPath query:

./following-sibling::node()[1]/self::text()[normalize-space()]

says:

  • . from the context node
  • following-sibling::node()[1] take the first following sibling node (whether it be a text node or an element (or even a comment))
  • self::text()[normalize-space()] take the "current" node if it is a text node and doesn't consist of only whitespace

Output is:

51 Gill $500 James

This will also handle the scenario where you could have a text node after the last child element of the parent <span id="outer">.

Xpath - select element with attribute and take another attribute value

Thought the issue is resolved, I just want to provide the idea how you can quickly check your xpath here.

This will give you the message if xpath is not correct and you can get idea what's wrong.

Sample Image

PHP Xpath: Get node value by class name

har07's answer was on the right track, but it only returned the node list with length set to 3 like I was already receiving with my existing code.

Original code:

$productPrice = $productRaw[1]->getElementsByTagName('li');

har07's suggestion:

$productPrice = $xpath->query('.//li[contains(@class, "itemtwo")]', $productRaw->item(1));

Solution, which returns the node value where an elements class name is equal to itemtwo:

$productPrice = $xpath->query('.//li[contains(@class, \'itemtwo\')]', $productRaw[1])->item(1)->nodeValue;

selecting first 6 element with xpath [position() = 6]

i think you need this.

  $route = $path->query("//li[1]/img[@class='lazy' and position() <= 6]");
foreach($route as $val){

var_dump(trim($val->getAttribute("data-original")));

}

if you don't want to specify the index of your <li> you need to use a counter.

 $route = $path->query("//li/img[@class='lazy' and position() <= 6]");
$ctr = 0;
foreach($route as $val){
if ($ctr <= 6){
var_dump(trim($val->getAttribute("data-original")));
}
$ctr = $ctr + 1;
}

How to get the next HTML element after a comment by XPath?

You could alter your XPath expression to use the following-sibling axis and say pick up the first node after the comment...

$comments = $xpath->query('//comment()/following-sibling::*[1]');

If you need the comment and the next node, you can first get the comment and then use XPath relative to that comment to fetch the following node...

$comments = $xpath->query('//comment()');
foreach ($comments as $comment)
{
echo $comment->nodeValue.PHP_EOL;
$next = $xpath->query("following-sibling::*[1]", $comment)[0];
echo $next->nodeValue.PHP_EOL;
}


Related Topics



Leave a reply



Submit