PHP SimpleXMLElement XPath Selection - php

I have the following XML: http://pastebin.com/QiRK72BK
which is generated in response to a REST query. My code is very simple:
$xml = simplexml_load_file($url);
var_dump($xml->getName());
var_dump($xml->xpath("serverDetail/apiEnv"));
in an attempted test case. The first var_dump reveals that the XML file is indeed being loaded:
string(21) "GeneralSearchResponse"
However, the second var_dump puzzles me. I feel it should definitely match some data, but instead I get
array(0) { }
I've also tried the xpath "/serverDetail/apiEnv" "//apiEnv" and "/" and always get an empty array. Am I misunderstanding xpath or perhaps missing some initialization step?

Your XML is using a namespace:
$xml->registerXPathNamespace('u', 'urn:types.partner.api.shopping.com');
var_dump($xml->xpath("//u:serverDetail/u:apiEnv"));
Output:
array(1) {
[0]=>
object(SimpleXMLElement)#2 (1) {
[0]=>
string(7) "sandbox"
}
}
Edit: Dirty workaround, might be helpful though:
$xml = simplexml_load_file($url);
$xmlStr = str_replace("xmlns", "ns", $xml->asXML());
$xml = new SimpleXMLElement($xmlStr);
var_dump($xml->xpath("serverDetail/apiEnv"));

Related

How to correctly write a DOMXPath query for this XML?

I'm writing a script to parse for this XML.
I want to parse all the <Contents> node with DOMDocument and DOMXpath. But for some reason, all the XPath queries I tried failed.
My code:
<?php
$apiUrl = 'https://chromedriver.storage.googleapis.com/?delimiter=/&prefix=98.0.4758.48/';
$xmlContents = file_get_contents($apiUrl);
if (!$xmlDom->loadXML($xmlContents)) {
throw new \Exception('Unable to parse the chromedriver file index API response as XML.');
}
$xpath = new \DOMXPath($xmlDom);
// **I tried several $query values here**
$fileEntries = $xpath->query($query, null, false);
if (!$fileEntries instanceof \DOMNodeList) {
throw new \Exception('Failed to evaulate the xpath into node list.');
}
echo "There are {$fileEntries->length} results\n";
foreach ($fileEntries as $node) {
/** #var \DOMNode $node */
var_dump($node->nodeName);
}
XPath $query I tried:
/ListBucketResult/Contents
/Contents
//Contents
All of these results in "There are 0 results".
If I use * in the $query, it will list all the nodes within the <ListBucketResult> root node:
There are 10 results
string(4) "Name"
string(6) "Prefix"
string(6) "Marker"
string(9) "Delimiter"
string(11) "IsTruncated"
string(8) "Contents"
string(8) "Contents"
string(8) "Contents"
string(8) "Contents"
string(8) "Contents"
The easy way is to filter the nodes with the nodeName attribute. But I do want to know what went wrong with my XPath query. What did I miss?
What you missed - because you didn't see it in the view given - is, that all nodes are in a namespace, because the root element really is
<ListBucketResult xmlns="http://doc.s3.amazonaws.com/2006-03-01">
So this element and all of its children are in the namespace http://doc.s3.amazonaws.com/2006-03-01. Adding a namespace like this
$xpath->registerNamespace("aws", "http://doc.s3.amazonaws.com/2006-03-01");
after $xpath = new DOMXPath($xmlDom); and using it in your XPath expressions like that
/aws:ListBucketResult/aws:Contents
should solve your problem.

How to convert XML with multiple prefixes to object

We have xml similiar to this:
<?xml version="1.0" encoding="UTF-8"?>
<animal:race>
<animal:dog>
<specification:color>Black</specification>
</animal:dog>
<animal:cat>
<animal:name>Flappie</animal:name>
</animal:cat>
</animal:race>
We tried simplexml_load_file("file.xml",null,null,"animal");
which returns
object(animal:race)#1 (2) {
["dog"]=> object(SimpleXMLElement)#2 (0) {
}
["cat"]=> object(SimpleXMLElement)#2 (1) {
[animal:name] => string "flappie"
}
}
We've seen multiple posts about this but can't seem to get any of this working stripping the prefixes away is not an option cause we need those in a later stage...
Can someone perhaps make a bit of code which converts this to an valid object so that we can adjust it to our own xml.

Reading XML file with namespaces

I need som help reading a XML that has namespaces.
I can read file with out any namepaces but not with namespaces..
XML sample:
<?xml version="1.0" encoding="utf-8"?>
<OrderResponse xmlns:cac="urn:basic:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:xsi="http://www.w3.org/" xmlns:cbc="urn:basic:names:specification:ubl:schema:xsd:BasicComponents-2" xmlns="urn:basic:names:specification:ubl:schema:xsd:OrderResponse-2">
<cbc:UBLVersionID>2.1</cbc:UBLVersionID>
<cbc:AccountingCostCode>TESTER TEST</cbc:AccountingCostCode>
<cac:OrderReference>
<cbc:ID>100067010</cbc:ID>
<cbc:IssueDate>2016-06-15</cbc:IssueDate>
<cbc:OrderTypeCode>EDI</cbc:OrderTypeCode>
</cac:OrderReference>
</OrderResponse>
I need to get the value of the ..
Im trying do it with DomDocument.
Here is my code:
function SearchXMLID($xml){
var_dump($xml);
$doc = new DOMDocument();
$doc->load($xml);
$id = $doc->getElementsByTagNameNS('urn:basic:names:specification:ubl:schema:xsd:CommonAggregateComponents-2','cbc:ID');
foreach($id as $i){
echo "<pre>";var_dump('NS',$i->nodeValue,PHP_EOL);"</pre>";
}
}
$files = glob('dataXMl/*xml');
echo "<pre>";var_dump($files,PHP_EOL);"</pre>";
foreach($files as $f){
SearchXMLID($f);
}
This code works but is getting all namespaces with 'cbc:' and stores the in a string..
array(1) {
[0]=>
string(17) "dataXMl/test1.xml"
}
string(1) "
"
string(17) "dataXMl/test1.xml"
string(2) "NS"
string(40) "
100000050
2016-06-15
EDI
"
string(1) "
"
It gets all tags with the namespace 'cbc'.. but i want to get the tag 'cbc:ID' only.
What am i doing wrong?
I'm no expert with php coding but my gut tells me that both of your parameters for getElementsByTagNameNS are wrong.
Try this:
$id = $doc->getElementsByTagNameNS('urn:basic:names:specification:ubl:schema:xsd:BasicComponents-2','ID');
i.e. use the correct namespace-uri: "urn:basic:names:specification:ubl:schema:xsd:BasicComponents-2"
and drop the cbc prefix

What's wrong with this attempt to use SimpleXML?

I can't seem to get anything to work with simpleXML for PHP. What is wrong with the following:
$xml = simplexml_load_string('<book><title>The Title</title></book>');
$title = $xml->book->title;
echo "<pre>title = $title\n</pre>";
The resulting output is:
title =
Why isn't the output as follows?
title = The Title
Please advise.
Since <book> is the root node of this snippet, you need $xml->title rather than $xml->book->title.
$xml = simplexml_load_string('<book><title>The Title</title></book>');
$title = $xml->title;
echo "<pre>title = $title\n</pre>";
// Prints
<pre>title = The Title
</pre>
The structure is more easily discovered if you var_dump() it:
var_dump($xml);
object(SimpleXMLElement)#1 (1) {
["title"]=>
string(9) "The Title"
}
Try
$str = '<book><title>The Title</title></book>';
$xml = new SimpleXMLElement($str);
$title = $xml->book->title;
echo $title;
What I suspect the problem being is that you haven't created the XML object and are trying to use a method from that object. Thats my assumption given your code snippet.
Take a look at PHP: Simple XML

PHP, SimpleXML arrays. Unanticipated casting of an array to a string

Sample XML:
<root>
<ratings>3</ratings>
<ratings>5</ratings>
<ratings>7</ratings>
</root>
The following code is the basis for my small application, it works as would be expected:
<?php
// $xml is some simplexml object
sizeof($xml->ratings); //3
foreach($xml->ratings as $rating){
echo($rating->value."::"); //this echoes all 3 rating values: 3::5::7
}
?>
This next code, which I would normally consider to be equivalent is not. And I have no idea why:
<?php
// $xml is some simplexml object
$ratings = $xml->ratings;
sizeof($ratings); //3, all is well so far
foreach($ratings as $rating){
echo($rating."::");
/*this echoes a never-ending list of ratings,
looking like 3::5::5::5::5::5::5::5...... */
}
?>
My feeling is that the assignment operator is casting the array of simplexml objects (ratings objects) as something odd, but have no clue as to how.
Other little hints:
var_dump($xml);
/* Output is:
object(SimpleXMLElement)#7 (1) {
["ratings"]=>
array(3) {
[0]=>
string(1) "3"
[1]=>
string(1) "5"
[2]=>
string(1) "7"
}
}
*/
var_dump($ratings);
/* Output is:
object(SimpleXMLElement)#6 (1) {
[0]=>
string(1) "3"
}
*/
Your echos are not the same:
echo($rating."::");
should be
echo($rating->value."::");
Ok, cleaning up some of my own work. After attempting to simplify my issue more, I was not able to prove it. After messing with the actual code, I assume this means I have some sort of mutating object elsewhere in my app that is going bonkers and creating weird results in this xml parsing. Sorry for the confusion and needless question (I guess this proves why i'm trying to refactor some of my complexity out of this app).
As a parting gift, here is the test-suite of code that I used (from simple to more realistic) that I used to prove that all worked as advertised:
<?php
$string = <<<XML
<?xml version='1.0'?>
<root>
<ratings>3</ratings>
<ratings>5</ratings>
<ratings>7</ratings>
</root>
XML;
$xml = simplexml_load_string($string);
var_dump($xml);
echo("Size:".sizeof($xml->ratings)."\n");
foreach($xml->ratings as $rating){
echo($rating."::");
}
echo("\n"."------"."\n");
$ratings = $xml->ratings;
echo("Size:".sizeof($ratings)."\n");
foreach($ratings as $rating){
echo($rating."::");
}
echo("\n\n\n\n"."||||New Example||||"."\n\n\n\n");
$stringthree = <<<XML
<root attr1="val" attr2="desc">
<field-one>val</field-one>
<elm-two attr-name="foo">elmTwoVal1</elm-two>
<elm-three>elmThreeVal1</elm-three>
<elm-two attr-name="bar">elmTwoVal2</elm-two>
<elm-three>elmThreeVa2</elm-three>
<elm-two attr-name="bear">elmTwoVal3</elm-two>
<elm-three>elmThreeVal3</elm-three>
</root>
XML;
$xmlthree = simplexml_load_string($stringthree);
var_dump($xmlthree);
echo("Size:".sizeof($xmlthree->{'elm-two'})."\n");
foreach($xmlthree->{'elm-two'} as $elm){
echo($elm."::");
}
echo("\n"."------"."\n");
$elmMain = $xmlthree->{'elm-two'};
echo("Size:".sizeof($elmMain)."\n");
foreach($elmMain as $elm){
echo($elm."::");
}
?>

Categories