Get parent data when child value matches - php

I would like to get parent data when my child value matches with "id22"
<servers>
<someservers>
<owner>id0</owner>
<serverCode>fefwewf</serverCode>
<address>345345</address>
<authCertHash>efref</authCertHash>
<authCertHash>erferf=</authCertHash>
<client>id1</client>
<client>id22</client>
</someservers>
<someservers>
<owner>id33</owner>
<serverCode>f</sewewefrverCode>
<address>234234234</address>
<authCertHash>sdfs</authCertHash>
<client>id27</client>
</someservers>
</server>
Currently im trying as following:
$server = $xml -> xpath('//someservers//client='id22');
echo $server->address;
But it's not working, I get error:
Trying to get property of non-object in...
I hope to get output:
345345

SimpleXMLElement::xpath() only supports Xpath expressions that return an node list, so it can convert them into an array of SimpleXMLElementinstances.
#andersson already explained that you expression only returns the existence of the specified client element. You need to use a condition. This will return an array with matching SimpleXMLElement objects as the elements.
$servers = new SimpleXMLElement($xml);
$server = $servers->xpath('//someservers[client="id22"]')[0];
echo $server->address;
Output:
345345
DOMXpath::evaluate() is able to return the scalar value directly:
$document = new DOMDocument();
$document->loadXml($xml);
$xpath= new DOMXpath($document);
echo $xpath->evaluate('string(//someservers[client="id22"]/address)');

Your XPath //someservers//client='id22 intend to return boolean true/false value, but not element or its text content
Try to use below XPath expression
//someservers[client='id22']/address/text()
that should return required "345345"

Related

Xpath accessing the value of an attribute within an XML tag

I'm attempting to extract the date saved in the <PersonDetails> tag for some XML I am working with, example:
<Record>
<PersonDetails RecordDate="2017-03-31T00:00:00">
<FirstName>Joe</FirstName>
<Surname>Blogs</Surname>
<Status>Active</Status>
</PersonDetails>
</Record>
Currently I have been trying the following:
if (isset($XML->Record->xpath("//PersonDetails[#RecordDate]")[0])) {
$theDate = $XML->Record->xpath("//PersonDetails[#RecordDate]")[0])->textContent;
} else {
$theDate = "no date";
}
My intention is to have $theDate = 2017-03-31T00:00:00
A valid XPath expression for selecting attribute node should look like below:
$theDate = $XML->xpath("//Record/PersonDetails/#RecordDate")[0];
echo $theDate; // 2017-03-31T00:00:00
You're mixing SimpleXML and DOM here. Additionally the expression fetches a PersonDetails element that has a RecordDate attribute. [] are conditions.
SimpleXML
So to fetch attribute node you need to use //PersonDetails/#RecordDate. In SimpleXML this will create a SimpleXMLElement for a non existing element node that will return the attribute value if cast to a string. SimpleXMLElement::xpath() will always return an array so you need to cast the first element of that array into a string.
$theDate = (string)$XML->xpath("//PersonDetails/#RecordDate")[0];
DOM
$textContent is a property of DOM nodes. It contains the text content of all descendant nodes. But you don't need it in this case. If you use DOMXpath::evaluate(), the Xpath expression can return the string value directly.
$document = new DOMDocument();
$document->loadXml($xmlString);
$xpath = new DOMXpath($document);
$theDate = $xpath->evaluate('string(//PersonDetails/#RecordDate)');
The string typecast is moved into the Xpath expression.

PHP XPath issue

Having a real bugger of an Xpath issue. I am trying to match the nodes with a certain value.
Here is an example XML fragment.
http://pastie.org/private/xrjb2ncya8rdm8rckrjqg
I am trying to match a given MatchNumber node value to see if there are two or more. Assuming that this is stored in a variable called $data I am using the below expression. Its been a while since ive done much XPath as most thing seem to be JSON these days so please excuse any rookie oversights.
$doc = new DOMDocument;
$doc->load($data);
$xpath = new DOMXPath($doc);
$result = $xpath->query("/CupRoundSpot/MatchNumber[.='1']");
I need to basically match any node that has a Match Number value of 1 and then determine if the result length is greater than 1 ( i.e. 2 or more have been found ).
Many thanks in advance for any help.
Your XML document has a default namespace: xmlns="http://www.fixtureslive.com/".
You have to register this namespace on the xpath element and use the (registered) prefix in your query.
$xpath->registerNamespace ('fl' , 'http://www.fixtureslive.com/');
$result = $xpath->query("/fl:ArrayOfCupRoundSpot/fl:CupRoundSpot/fl:MatchNumber[.='1']");
foreach( $result as $e ) {
echo '.';
}
The following XPath:
/CupRoundSpot[MatchNumber = 1]
Returns all the CupRoundSpot nodes where MatchNumber equals 1. You could use these nodes futher in your PHP to do stuff with it.
Executing:
count(/CupRoundSpot[MatchNumber = 1])
Returns you the total CupRoundSpot nodes found where MatchNumber equals 1.
You have to register the namespace. After that you can use the Xpath count() function. An expression like that will only work with evaluate(), not with query(). query() can only return node lists, not scalar values.
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
$xpath->registerNamespace('fl', 'http://www.fixtureslive.com/');
var_dump(
$xpath->evaluate(
'count(/fl:ArrayOfCupRoundSpot/fl:CupRoundSpot[number(fl:MatchNumber) = 1])'
)
);
Output:
float(2)
DEMO: https://eval.in/130366
To iterate the CupRoundSpot nodes, just use foreach:
$nodes = $xpath->evaluate(
'/fl:ArrayOfCupRoundSpot/fl:CupRoundSpot[number(fl:MatchNumber) = 1]'
);
foreach ($nodes as $node) {
//...
}

How to get perticular node value? in xml using php

I am using xml data using a url & because the xml is too long I just want to check condition to get particular node values only :-
Here is my code :-
<?php
$doc = new DOMDocument('1.0', 'utf-8');
$doc->load("https://retailapi.apparel21.com/RetailAPI/products?countrycode=au");
$xpath = new DOMXpath($doc);
/*foreach ($xpath->query("/Products/Product[Code='00122']") as $node)
{
echo $node->nodeValue;
echo "Hi<br>";
}*/
echo $xpath->query("/Products/Product[Code='00122']")->item(0)->nodeValue;
?>
As you can see that I already used foreach loop & successfully executed the condition but.....the thing is inside it, it prints whole data of that all the nodes of it's parent node.
Confused? :)
Ok no worries, just execute this url: https://retailapi.apparel21.com/RetailAPI/products?countrycode=au; please click on Proceed anyway button then wait for some time.
There are many Product tags...now I want the data of the following nodes :-
Id
Code
Name
Description
whose code=00122 that's the first product's data.
I applied foreach then it printed all node's data of that product. I applied simple single statement but then also it printed all node's data :(
And one more thing is can't it be done by simplexml_load_file function?
One more thing :- You can see I am loading url, so the thing is it will read the whole xml first. Can't we query in this itself so that it will only take only related product tags so the loading time can be reduced.
Can anyone please help?
You're nearly there. Replace DOMXpath::query() with DOMXpath::evaluate(). It allows to use Xpath expressions that return scalars like strings. Now the second argument of evaluate() (or query()) is the context, so you can iterate all nodes from one expression and fetch the details using xpath expressions depending on a context node:
$doc = new DOMDocument('1.0', 'utf-8');
$doc->load("https://retailapi.apparel21.com/RetailAPI/products?countrycode=au");
$xpath = new DOMXpath($doc);
$result = [];
foreach ($xpath->evaluate("/Products/Product[Code='00122']") as $node) {
$result[] = [
'id' => $xpath->evaluate('string(Id)', $node),
'code' => $xpath->evaluate('string(Code)', $node),
'name' => $xpath->evaluate('string(Name)', $node),
];
}
var_dump($result);
A call like $xpath->evaluate('Id', $node) would return a list with all Id element nodes that are children of $node. The Xpath function string() casts the first node in this list into a string and returns it. If the list is empty the result will be an empty string.

return result of XPath function via SimpleXML

It seems that PHP SimpleXML XPath doesn't allow to get results of XPath functions:
$s = new \SimpleXMLElement('<test><node>A</node><node>B</node></test>');
var_dump($s->xpath("count(node)"));
Returns an empty array:
array(0) {
}
While Using DOM returns the expected value 2:
$dom = new \DOMDocument();
$dom->loadXML('<test><node>A</node><node>B</node></test>');
$xpath = new \DOMXPath($dom);
var_dump($xpath->evaluate("count(node)"));
float(2.0)
Is there a way to do the same directly with SimpleXML?
PHP's SimpleXML only works on queries which return nodesets. count(...) returns a scalar value which is not supported. Use DOMXPath which is much more capable or count the objects in the result array:
var_dump(count($s->xpath("node")));
int(2)

How to get full HTML from DOMXPath::query() method?

I have document from which I want to extract specific div with it's untouched content.
I do:
$dom = new DOMDocument();
$dom->loadHTML($string);//that's HTML of my document, string
and xpath query:
$xpath = new DOMXPath($dom);
$xpath_resultset = $xpath->query("//div[#class='text']");
/*I'm after div class="text"*/
now I do item(0) method on what I get with $xpath_resultset
$my_content = $xpath_resultset->item(0);
what I get is object (not string) $my_content which I can echo or settype() to string, but as result I get is with fully stripped markup?
What to do to get all from div class='text' here?
Just pass the node to the DOMDocument::saveHTML method:
$htmlString = $dom->saveHTML($xpath_resultset->item(0));
This will give you a string representation of that particular DOMNode and all its children.

Categories