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)
Related
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"
I have a xml response like that;
<n:Crev xmlns:soap="http://a.com"
xmlns:obj="http://b.com"
xmlns:n="http://c.com"
xmlns:msg="http://d.com"
xmlns="http://e.com"
xmlns:xsi="http://f.com"
xsi:schemaLocation="http://g.com">
<n:Header>
<msg:mydata>123123</msg:mydata>
</n:Header>
</n:Crev>
now I want to get 'msg:mydata' value..
I tried some xpaths but they didn't work and tried online xpath creator it gives something like;
'/n:Crev[#xmlns:soap="http://a.com"]/n:Header/msg:mydata/text()'
but it didn't work also.. So how can I write xpath for that?
Thanks
I've succeeded with following code:
<?php
$xmlStr = '<n:Crev xmlns:soap="http://a.com"
xmlns:obj="http://b.com"
xmlns:n="http://c.com"
xmlns:msg="http://d.com"
xmlns="http://e.com"
xmlns:xsi="http://f.com"
xsi:schemaLocation="http://g.com">
<n:Header>
<msg:mydata>123123</msg:mydata>
</n:Header>
</n:Crev>';
$xmlDoc = new DOMDocument();
$xmlDoc->loadXML($xmlStr);
$xmlPath = new DOMXPath($xmlDoc);
var_dump($xmlPath->query('//n:Crev/n:Header/msg:mydata')->item(0)->textContent);
result:
string '123123' (length=6)
n or msg are namespace prefixes. The actual namespaces are the values of the xmlns attributes. The XML parser will resolve the namespaces.
Here is a small example:
$document = new DOMDocument();
$document->loadXml('<n:Crev xmlns:n="http://c.com"/>');
var_dump(
$document->documentElement->namespaceURI,
$document->documentElement->localName
);
Output:
string(12) "http://c.com"
string(4) "Crev"
The following XMLs all would have the same output:
<n:Crev xmlns:n="http://c.com"/>
<Crev xmlns="http://c.com"/>
<c:Crev xmlns:c="http://c.com"/>
You can read the node as {http://c.com}Crev.
To fetch nodes or scalar values from the DOM you can use Xpath::evaluate(). But to match namespaces you will have to register prefixes for the Xpath expressions. This allows the Xpath engine to resolve the namespaces and match them against the node properties. The prefixes do not have to be the same as in the document.
$xml = <<<'XML'
<n:Crev xmlns:n="http://c.com" xmlns:msg="http://d.com">
<n:Header>
<msg:mydata>123123</msg:mydata>
</n:Header>
</n:Crev>
XML;
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('c', 'http://c.com');
$xpath->registerNamespace('msg', 'http://d.com');
var_dump(
$xpath->evaluate('string(/c:Crev/c:Header/msg:mydata)')
);
Output:
string(6) "123123"
If the expression is an location path like /c:Crev/c:Header/msg:mydata the result with be an DOMNodeList, but Xpath functions or operators can return scalar values.
I'm trying to parse a remote XML file, which is valid:
$xml = simplexml_load_file('http://feeds.feedburner.com/HammersInTheHeart?format=xml');
The root element is feed, and I'm trying to grab it via:
$nodes = $xml->xpath('/feed'); //also tried 'feed', without slash
Except it doesn't find any nodes.
print_r($nodes); //empty array
Or any nodes of any kind, so long as I search for them by tag name, in fact:
$nodes = $xml->xpath('//entry');
print_r($nodes); //empty array
It does find nodes, however, if I use wildcards, e.g.
$nodes = $xml->xpath('/*/*[4]');
print_r($nodes); //node found
What's going on?
Unlike DOM, SimpleXML has no concept of a document object, only elements. So if you load an XML you always get the document element.
$feed = simplexml_load_file($xmlFile);
var_dump($feed->getName());
Output:
string(4) "feed"
That means that all Xpath expression have to to be relative to this element or absolute. Simple feed will not work because the context already is the feed element.
But here is another reason. The URL is an Atom feed. So the XML elements in the namespace http://www.w3.org/2005/Atom. SimpleXMLs magic syntax recognizes a default namespace for some calls - but Xpath does not. Here is not default namespace in Xpath. You will have to register them with a prefix and use that prefix in your Xpath expressions.
$feed = simplexml_load_file($xmlFile);
$feed->registerXpathNamespace('a', 'http://www.w3.org/2005/Atom');
foreach ($feed->xpath('/a:feed/a:entry[position() < 3]') as $entry) {
var_dump((string)$entry->title);
}
Output:
string(24) "Sharing the goals around"
string(34) "Kouyate inspires Hammers' comeback"
However in SimpleXML the registration has to be done for each object you call the xpath() method on.
Using Xpath with DOM is slightly different but a lot more powerful.
$document = new DOMDocument();
$document->load($xmlFile);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('a', 'http://www.w3.org/2005/Atom');
foreach ($xpath->evaluate('/a:feed/a:entry[position() < 3]') as $entry) {
var_dump($xpath->evaluate('string(a:title)', $entry));
}
Output:
string(24) "Sharing the goals around"
string(34) "Kouyate inspires Hammers' comeback"
Xpath expression using with DOMXpath::evaluate() can return scalar values.
This question already has answers here:
SimpleXML: Selecting Elements Which Have A Certain Attribute Value
(2 answers)
Closed 8 years ago.
I am new to processing and reading XML strings in PHP
I have XML like this
<sports-metadata>
<sports-content-codes>
<sports-content-code code-type="sport" code-key="15027000" code-name="Golf"/>
<sports-content-code code-type="league" code-key="l.pga.com" code-name="Professional Golf Association"/>
<sports-content-code code-type="season-type" code-key="regular"/>
<sports-content-code code-type="season" code-key="2015"/>
<sports-content-code code-type="priority" code-key="normal"/>
</sports-content-codes>
</sports-metadata>
I have read in the XML via a $xml=simplexml_load_file()
I can get to this XML section via $xml->{'sports-content-codes'}->{'sports-content-code'}
In sports-content-code
I want to access/retrieve the code-key value where code-type="season"
How can I do this in PHP?
Thank you all.
-- Ed
Usually you use ->attributes() method to get those attributes:
foreach($xml->{'sports-content-codes'}->{'sports-content-code'} as $content_code) {
$attr = $content_code->attributes();
$code_type = (string) $attr->{'code-type'};
echo $code_type;
}
Sample Output
Use Xpath ...
... with SimpleXml:
$element = simplexml_load_string($xml);
$array = $element->xpath(
'//sports-content-code[#code-type="season"]'
);
var_dump(
(string)$array[0]['code-key']
);
... or DOM:
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
var_dump(
$xpath->evaluate(
'string(//sports-content-code[#code-type="season"]/#code-key)'
)
);
Output (both):
string(4) "2015"
Xpath is a expression language for DOM (think SQL for DBMS). SimpleXMLElement::xpath() supports some of it (only expressions that return element or attribute nodes). The result will always be an array of SimpleXMLElement objects. DOMXpath::evaluate() supports full Xpath 1.0. The result is a DOMNodelist or a scalar value, depending on the expression.
The expression:
Select the "sports-content-code" element nodes
//sports-content-code
with a "code-type" attribute node is season
//sports-content-code[#code-type="season"]
get the "code-key" attribute nodes
//sports-content-code[#code-type="season"]/#code-key
cast the node list to a string returning the text content of the first node
string(//sports-content-code[#code-type="season"]/#code-key)
I'd like to search for nodes with the same node name in a SimpleXML Object no matter how deep they are nested and create an instance of them as an array.
In the HTML DOM I can do that with JavaScript by using getElementsByTagName(). Is there a way to do that in PHP as well?
Yes use xpath
$xml->xpath('//div');
Here $xml is your SimpleXML object.
In this example you will get array of all 'div' elements
$fname = dirname(__FILE__) . '\\xml\\crRoll.xml';
$dom = new DOMDocument;
$dom->load($fname, LIBXML_DTDLOAD|LIBXML_DTDATTR);
$root = $dom->documentElement;
$xpath = new DOMXpath($dom);
$xpath->registerNamespace('cr', "http://www.w3.org/1999/xhtml");
$candidateNodes = $xpath->query("//cr:break");
foreach ($candidateNodes as $child) {
$max = $child->getAttribute('tstamp');
}
This finds all the BREAK nodes (tstamp attr) using XPath ...
Only on DOMDocument::getElementsByTagName,
however, you can import/export SimpleXML into DOMDocument,
or simply use DOMDocument to parse XML.
Another answer mentioned about Xpath,
it will return duplication of node, if you have something like :-
<div><div>1</div></div>