SimpleXMLElement - Trying to get property of non-object - php

In a response we receive an xml file, then convert to SimpleXMLElement, then access the elements and attributes as needed. However, we are getting "Trying to get property of non-object" when xml is loaded directly from string response vs from saved response.
//This code works
$response = simplexml_load_file( "response.xml" );
var_dump($response);
echo $response->RESPONSE->RESPONSE_DATA->FILE_INFORMATION['Order_Number'];
//Returns
//object(SimpleXMLElement)#153 (4) { ["#attributes"]=> array(1)...the rest of the xml file...
//Order_Number
//This code returns error
$response = simplexml_load_string( $response );
var_dump($response);
echo $response->RESPONSE->RESPONSE_DATA->FILE_INFORMATION['Order_Number'];
//Returns
//object(SimpleXMLElement)#153 (1) { [0]=> string(33864) "" }
//Notice: Trying to get property of non-object in...
What would cause the xml to fail when using simplexml_load_string instead of simplexml_load_file?
Here is a snippet of the xml file:
<?xml version="1.0" encoding="UTF-8"?>
<RESPONSE_GROUP>
<RESPONSE>
<RESPONSE_DATA>
<FILE_INFORMATION Order_Number="19222835">
...
</FILE_INFORMATION>
</RESPONSE_DATA>
</RESPONSE>
</RESPONSE_GROUP>

You've just overlooked a little detail here. It is correct what you say for the first part:
$response = simplexml_load_file( "response.xml" );
This loads the XML document from the file. However when you look at the second part:
$response = simplexml_load_string( $response );
You don't load from string response. $response represents the SimpleXMLElement you've just created from file. The more "correct" example is:
$buffer = file_get_contents( "response.xml" );
$response = simplexml_load_string( $buffer );
You perhaps just were confused due to variable-reuse (taken the same named variable for two different things).
And better as var_dump is to check with $response->asXML() as it will show you the document as XML which shows better what you have (or not).

This works for me:
<?php
$response = '<?xml version="1.0" encoding="UTF-8"?>
<RESPONSE_GROUP>
<RESPONSE>
<RESPONSE_DATA>
<FILE_INFORMATION Order_Number="19222835">
...
</FILE_INFORMATION>
</RESPONSE_DATA>
</RESPONSE>
</RESPONSE_GROUP>';
//This code returns error
$response = simplexml_load_string( $response );
var_dump($response);
echo $response->RESPONSE->RESPONSE_DATA->FILE_INFORMATION['Order_Number'];
?>
Output:
object(SimpleXMLElement)#1 (1) {
["RESPONSE"]=>
object(SimpleXMLElement)#2 (1) {
["RESPONSE_DATA"]=>
object(SimpleXMLElement)#3 (1) {
["FILE_INFORMATION"]=>
string(33) "
...
"
}
}
}
19222835

Related

Can't get node's text with XPATH from my XML using PHP

Hey everybody thanks for your time, I'm trying to parse a big XML file (pic bellow) and to get the text of specific nodes using XPATH expression in PHP.
Here is my php :
<?php
echo "[Generation Starts !]\n";
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
if (file_exists('../source/particuliers/arborescence.xml')) {
$xml = new SimpleXMLElement(file_get_contents('../source/particuliers/arborescence.xml'));
$xml->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
$themes = $xml->xpath("/Arborescence/Item[#type='Theme']/Titre/text()");
var_dump($themes);
$JSON = json_encode($themes, JSON_UNESCAPED_UNICODE);
file_put_contents('testing.json', $JSON);
echo "[Generation Done !]\n";
} else {
echo "File wasn't found\n";
}
I won't put the whole XML file here as it is way too big but here is a pic so you see the structure
Using this XPATH expression /Arborescence/Item[#type='Theme']/Titre/text() I expected to get text from my nodes but I only have an empty array with the right number of elements in it but all empty.
Anything I'm doing wrong ?
The result of SimpleXMLElement::xpath() is always an array of SimpleXMLElement objects (or false for an invalid expression). SimpleXMLElement object represent element nodes, but the extension does some magic for text nodes and attributes.
Stripping down the code from the question into an example:
$xml = <<<'XML'
<Arborescence>
<Item type="Theme">
<Titre>Loisirs</Titre>
</Item>
</Arborescence>
XML;
$xml = new SimpleXMLElement($xml);
$themes = $xml->xpath("/Arborescence/Item[#type='Theme']/Titre/text()");
var_dump($themes);
Output:
array(1) {
[0]=>
object(SimpleXMLElement)#2 (1) {
[0]=>
string(7) "Loisirs"
}
}
The result is an array with a single SimpleXMLElement that contains the text. You can use array_map() to cast all returned objects to strings.
$xml = new SimpleXMLElement($xml);
$themes = array_map(
function(SimpleXMLElement $element) {
return (string)$element;
},
$xml->xpath("/Arborescence/Item[#type='Theme']/Titre/text()")
);
Output:
array(1) {
[0]=>
string(7) "Loisirs"
}

Specific field from XML into PHP

I am pretty new to XML sheets in combination with PHP. I am trying to pull data from an XML file that is being returned to me via SOAP call.
My XML is being returned as this.
commented out some of the details
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<loginResponse>
<result>
<metadataServerUrl>https://...-api.salesforce.com/service...</metadataServerUrl>
<passwordExpired>false</passwordExpired>
<sandbox>false</sandbox>
<serverUrl>https://...--api.salesforce.com/services/Soap/u/21.0/0...</serverUrl>
<sessionId>....</sessionId>
<userId>....</userId>
<userInfo>
<accessibilityMode>false</accessibilityMode>
<currencySymbol>€</currencySymbol> ... </userInfo>
</result>
</loginResponse>
</soapenv:Body>
</soapenv:Envelope>
So I am trying to pull out of this the sessionID
// UP HERE SOAP CALL --- return data
.......
} else {
$response = curl_exec($soap_do);
curl_close($soap_do);
// print($response); <-- see result XML
// grabbing the sessionid
$xmlresponse = new SimpleXMLElement($response);
$test = $xmlresponse->result->sessionId['value'];
echo $test;
}
This returns blank, but when I start adding the LoginResponse and the Soapenv (body and envelope), i get an error about that I am trying to get a propperty of non-object. I am not sure what I am doing wrong here.
With SimpleXML you can use SimpleXMLElement::children to find children by an XML namespace (here soapenv).
For your case it would something like
$xmlresponse = new SimpleXMLElement($response);
$response = $xmlresponse->children('soapenv', true)->Body->children('', true)->loginResponse->result->sessionId;
var_dump($response);
Which results in
object(SimpleXMLElement)#4 (1) {
[0]=>
string(4) "...."
}
I want to say that you should use SoapClient(http://php.net/manual/tr/class.soapclient.php) for Soap Calls but if you don't want to use it, here is how you can parse this XML :
$xmlresponse = new SimpleXMLElement(str_ireplace([':Envelope', ':Body'], '', $response));
$test = $xmlresponse->soapenv->loginResponse->result->sessionId['value'];
echo $test;

Paring XML loaded by file_get_contents()

I have some simple XML parser
$doc = new DOMDocument();
$doc->load("xml/UploadTask.xml");
$items = $doc->getElementsByTagName( "item" );
foreach( $items as $item )
{
$keys = $item->getElementsByTagName( "key" );
$key = $keys->item(0)->nodeValue;
$values = $item->getElementsByTagName( "value" );
$value = $values->item(0)->nodeValue;
echo "$key - $value";
}
and it work perfectly for me, how ever if i need to parse XML which comes via POST, my code to load XML looks like (I put it on top of file):
$xml = file_get_contents('php://input');
echo $xml;
and this return XML which comes, everything works right , how ever if I load $xml to DOM (basicly replace 2. line on $doc->load($xml);I'm getting no output, no echo of values.
while I try to debug this I notice that if I use file var_dump($items)
i get answer:
object(DOMNodeList)#2 (1) {
["length"]=>
int(0)
}
if I do same var_dump() with file loaded i get answer:
object(DOMNodeList)#2 (1) {
["length"]=>
int(2)
}
where int(2) represent number of matches for this criteria found.
So my question is are there some limits for parsing XML loading with file_get_contents ?
and also, is there any workaround on this?
Thanks guys.
DOMDocument::load() loads a file (or an URL using the Streamwrappers), not a string. DOMDocument::loadXml() loads a string.
$xml = '<foo>Hello World</foo>';
$document = new DOMDocument();
$document->loadXml($xml);
var_dump($document->documentElement->textContent);
Output:
string(11) "Hello World"

simplexml_load_file does not recognize <dc:title> tags

I am trying to use simplexml_load_file() to load an XML file and reach <dc:title> tags, but when I dump the $xml variable that loaded my xml file all the tags that starts with <dc: doesn't show. Is there a way of telling the parser to include them? should I use a different parser?
Here's my example file's code:
<?xml version='1.0' encoding='utf-8'?>
<package xmlns="http://www.idpf.org/2007/opf" prefix="ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/" unique-identifier="pub-id" version="3.0" xml:lang="en">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title>test</dc:title>
</metadata>
</package>
Code I used to check for which tags are seen:
$xml = simplexml_load_file('test.xml');
echo '<pre>';var_dump($xml);echo '</pre>';
And the result:
object(SimpleXMLElement)#1 (2) {
["#attributes"]=>
array(3) {
["prefix"]=>
string(80) "ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/"
["unique-identifier"]=>
string(6) "pub-id"
["version"]=>
string(3) "3.0"
}
["metadata"]=>
object(SimpleXMLElement)#2 (0) {
}
}
metadata is in fact, empty. Thoughts?
First, dc is a namespace, not part of tag name. For testing purpose you can print the namespaces used in the given xml. Executing the snippet
$namespaces = $xml->getNamespaces(true);
var_dump($namespaces);
will output
array(3) {
'' =>
string(28) "http://www.idpf.org/2007/opf"
'xml' =>
string(36) "http://www.w3.org/XML/1998/namespace"
'dc' =>
string(32) "http://purl.org/dc/elements/1.1/"
}
You have to register a prefix for the namespace for usage with xpath, then you can use it for selection:
$xml->registerXPathNamespace('b', 'http://purl.org/dc/elements/1.1/');
$a =$xml->xpath('//b:*/text()');
foreach($a as $t) {
print($t."\n");
}
This will output test of the element you want to get.
You can use
$xml = simplexml_load_file('test.xml');
$result = $xml->metadata->children('dc', true);
result of the code above is an object, now convert to array
$array = json_decode(json_encode($result),TRUE);
Result of $array
array(1) {
["title"]=>string(4) "test"
}

SimpleXML Returns 0 Element

I'm taking my response from a Soap Request, and passing it into a new SimpleXML construct.
$response = $this->client->$method(array("Request" => $this->params));
$response_string = $this->client->__getLastResponse();
$this->response = new Classes_Result(new SimpleXMLElement($result));
If I echo the $response_string, it outputs a proper xml string. Here is a snippet as it's quite long.
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body><GetClassesResponse xmlns="http://clients.mindbodyonline.com/api/0_5">
<GetClassesResult>
<Status>Success</Status>
<XMLDetail>Full</XMLDetail>
<ResultCount>6</ResultCount>
<CurrentPageIndex>0</CurrentPageIndex>
<TotalPageCount>1</TotalPageCount>
<Classes>
<Class>
<ClassScheduleID>4</ClassScheduleID>
<Location>
<SiteID>20853</SiteID>
....</soap:Envelope>
Hoever, when I try to work with this object, I get errors or if I dump the object it outputs:
object(SimpleXMLElement)#51 (0)
Any ideas why this might be happening?
You are not actually using $response_string, and you have not set $result anywhere, which you have passed to new SimpleXMLElement($result).
Perhaps you intend to build a SimpleXML object with the $response_string string via simplexml_load_string()?
$response = $this->client->$method(array("Request" => $this->params));
$response_string = $this->client->__getLastResponse();
// Load XML via simplexml_load_string()
$this->response = new Classes_Result(simplexml_load_string($response_string));
// Or if you do expect a SimpleXMLElement(), pass in the string
$this->response = new Classes_Result(new SimpleXMLElement($response_string));
The <soap:Body> element of your SOAP response is namespaced with (soap). To loop over it with SimpleXML, you must provide the correct namespace:
// After creating new SimpleXMLElement()
var_dump($this->response->children("http://schemas.xmlsoap.org/soap/envelope/"));
// class SimpleXMLElement#2 (1) {
// public $Body =>
// class SimpleXMLElement#4 (0) {
// }
// }
To loop over the body:
foreach ($this->response->children("http://schemas.xmlsoap.org/soap/envelope/") as $b) {
$body_nodes = $b->children();
// Get somethign specific
foreach ($body_nodes->GetClassesResponse->GetClassesResult as $bn) {
echo $bn->ResultCount . ", ";
echo $bn->TotalPageCount;
}
}
// 6, 1

Categories