Parse SOAP response with PHP/xpath - Cant step further than one node - php

I've tried to read other tutorials & SO responses on this but I just can't seem to make it work :/
I'm able to make the SOAP request and get a response but I can't seem to parse the response.
$result = $client->GetAllAttributes($params);
And the resulting response xml is:
<?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>
<GetAllAttributesResponse xmlns="http://connect2.askadmissions.net/webservices/">
<GetAllAttributesResult>
<result>
<code>1</code>
<ErrorMessage />
<returndata>
<attributes>
<attribute>
<type>attribute</type>
<level />
<name>text1321</name>
<mappingname><![CDATA[Extra-Curricular Interest]]></mappingname>
<datatype>Varchar2</datatype>
<size>35</size>
<validationexp />
</attribute>
<attribute> (same as above, several of these are returned</attribute>
</attributes>
</returndata>
</result>
</GetAllAttributesResult>
</GetAllAttributesResponse>
</soap:Body>
</soap:Envelope>
</xml>
I've tried
$xml = simplexml_load_string($client->__getLastResponse());
print_r($xml);
But it just prints "SimpleXMLElement Object ( ) "
I've tried
$responseXML = $client->__getLastResponse();
$xml = simplexml_load_string($responseXML);
$xml->registerXPathNamespace('soap', 'http://schemas.xmlsoap.org/soap/envelope/');
$xml->registerXPathNamespace('hob', 'http://connect2.askadmissions.net/webservices/');
$item = $xml->xpath('//hob:GetAllAttributesResult');
print_r($item);
and I get an array
Array
(
[0] => SimpleXMLElement Object
(
[0] => <result><code>1</code><ErrorMessage /><returndata><attributes><attribute> <type>attribute</type><level />
etc. (array is very long)
My problem comes when I try to step further into the tree. If I do
$item = $xml->xpath('//hob:GetAllAttributesResult/hob:result');
or
$item = $xml->xpath('//hob:GetAllAttributesResult/hob:code');
I end up with an empty array.
How do I step further into the tree?
Thank you very much for any help.

To access the element's value, you need to access the first element of the array. And you may need to cast it to string to get the string value
e.g. in your example you can do:
$item = $xml->xpath('//hob:GetAllAttributesResult/hob:result/hob:code');
print_r((string)$item[0]);

Related

simplexml_load_string return empty result

It may be simple, but I am kind of stuck. I want to convert an XML string into PHP object. My XML string is:
$a = '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<VerifyTxnResponse xmlns="http://www.exmlplekhe.com/">
<VerifyTxnResult>BID <11467></VerifyTxnResult>
</VerifyTxnResponse>
</soap:Body>
</soap:Envelope>';
I have tried var_dump(simplexml_load_string($a));, but it returns empty SimpleXMLElement object. I want to get VerifyTxnResult node. I think, <11467> is causing the problem. What may be the possible solution?
Thanks.
I want to get VerifyTxnResult node
The simplexml_load_string function returns an instance of SimpleXMLElement which is actually not empty for the XML you posted.
Register the namespace and fetch the node with xpath method:
$se = simplexml_load_string($a);
$se->registerXPathNamespace('r', 'http://www.nibl.com.np/');
foreach ($se->xpath('//r:VerifyTxnResult') as $result) {
var_dump((string)$result);
}
Sample Output
string(11) "BID <11467>"

PHP - SOAP xml returning empty when SimpleXmlElement object created

I've been trying to figure this out for over an hour now, and I give up. I'm getting the following response from a webservice.
I want to be able to get access to the children of Fault (I have this working for another webservice call, but for some reason, which I believe MIGHT be namespace related, this one doesn't work).
<soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Invalid Scope</faultstring>
<detail>
<faultresponse xmlns="xsd url" xmlns:ns2="xsd url">
<transactionid>TEST</transactionid>
<errorcode>ERR-4223</errorcode>
</faultresponse>
</detail>
</soap:Fault>
</soap:Body>
</soap:envelope>
My PHP code is pretty simple. Firstly, I create the SimpleXmlElement object from the above xml.
I get a list of namespaces in the xml (which it picks up two: "soap", and ""). I then get the children of the XML in the soap: namespace.
$xml = simplexml_load_string($response_xml);
$namespace = $xml->getNamespaces(true);
$soap = $xml->children($namespace['soap']);
Given the above code. I would expect to be able to do something like this:
$fault_fields = $soap->Body->children()->Fault->children();
foreach ($fault_fields as $field):
echo (string) $field->getName() . ' - ' . $field[0] . '<br />';
endforeach;
However, if I run $soap->asXML(); I can see the following:
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Invalid Scope</faultstring>
<detail>
<faultresponse xmlns="xsd url" xmlns:ns2="xsd url">
<transactionid>TEST</transactionid>
<errorcode>ERR-4223</errorcode>
</faultresponse>
</detail>
</soap:Fault>
</soap:Body>
But if I try to go to access Body or Fault:
$soap = &$this->parse_soap_body($response_xml);
$body = $soap->Body->children()->Fault->children();
echo $body->asXML();
I get a Node no longer exists error with the stack returning this XML.
<soap:Envelope xmlns:soap="`http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Invalid Scope</faultstring>
.... more xml
This is seriously doing my head in.
Any help would be greatly appreciated.

SoapClient XML return string with simpleXML

So I'm getting an xml file back from a soap service (of which I have no control over). It's returning back an xmlns which is causing simpleXML issues. I'm running a str_replace to get rid of that issue, however now simpleXML just returns an empty object. XML structure appears to be fine, no errors just an empty object.
$xmlString = $client->__getLastResponse();
$feed = str_replace(' xmlns="LMSWebservice"', '', $xmlString);
$sData= simplexml_load_string($feed);
print_r($sData);
Returns: SimpleXMLElement Object()
XML source before str replace is:
<?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>
<GetUserInternalIDByPersonalIDResponse xmlns="LMSWebservice">
<GetUserInternalIDByPersonalIDResult>
<Response xmlns="">
<Timestamp date="24/10/2013" time="04:27:37" />
<User>
<UserInternalID>4907</UserInternalID>
</User>
</Response>
</GetUserInternalIDByPersonalIDResult>
</GetUserInternalIDByPersonalIDResponse>
</soap:Body>
</soap:Envelope>
After str replace:
<?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>
<GetUserInternalIDByPersonalIDResponse>
<GetUserInternalIDByPersonalIDResult>
<Response xmlns="">
<Timestamp date="24/10/2013" time="04:27:37" />
<User>
<UserInternalID>4907</UserInternalID>
</User>
</Response>
</GetUserInternalIDByPersonalIDResult>
</GetUserInternalIDByPersonalIDResponse>
</soap:Body>
</soap:Envelope>
Any help would be greatly appreciated, this is driving me crazy!
----So if I don't get rid of the namespace attribute I get this error message:
Warning: simplexml_load_string() [function.simplexml-load-string]: Entity: line 1: parser warning : xmlns: URI LMSWebservice is not absolute in serviceTest2.php on line 16
Warning: simplexml_load_string() [function.simplexml-load-string]: LSchema"><soap:Body><GetUserInternalIDByPersonalIDResponse xmlns="LMSWebservice" in serviceTest2.php on line 16
Warning: simplexml_load_string() [function.simplexml-load-string]: ^ in serviceTest2.php on line 16
If I try using xPath to get at UserInternalID it returns an empty array. If what you're saying is correct and this is going into simpleXML correctly, then how do I access the UserInternalID node? Sorry, this is the first time using simpleXML and this is just perplexing me.
So just tried changing the NS
$feed = str_replace('xmlns="LMSWebservice"', 'xmlns="ns:LMSWebservice"', $xmlString);
which goes in without errors.
I tried this for the xPath
$ID = $sData->xpath('UserInternalID');
I understand this is probably completely wrong, but I haven't tried much else with this as it didn't seem to be going into simpleXML correctly in the first place. :/
So I've used
$ID = $sData->xpath('//UserInternalID');
echo $ID[0];
Which works perfectly. Thank you for all your help!
Through the extensive comments and your last edit finally the actual cause of your problem could be revealed, the xpath expression is wrong:
$ID = $sData->xpath('UserInternalID');
Wrong with it is the path, this matches no elements. Instead you could use:
$ID = (string) $xml->xpath('//UserInternalID')[0];
Or more verbose:
$ID = (string) $xml->xpath('//Response/User/UserInternalID')[0];
Key point here is that you write the correct path to the element you would like to query for.
Complete usage example:
<?php
/**
* SoapClient xml return string with simpleXML
*
* #link http://stackoverflow.com/q/19556414/367456
*/
$response = <<<RESPONSE
<?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>
<GetUserInternalIDByPersonalIDResponse xmlns="LMSWebservice">
<GetUserInternalIDByPersonalIDResult>
<Response xmlns="">
<Timestamp date="24/10/2013" time="04:27:37" />
<User>
<UserInternalID>4907</UserInternalID>
</User>
</Response>
</GetUserInternalIDByPersonalIDResult>
</GetUserInternalIDByPersonalIDResponse>
</soap:Body>
</soap:Envelope>
RESPONSE;
$restore = libxml_use_internal_errors(TRUE);
$xml = simplexml_load_string($response);
libxml_use_internal_errors($restore);
echo $xml->xpath('//Response/User/UserInternalID')[0];
Output:
4907
Online Demo: https://eval.in/57149
$doc = new DOMDocument();
libxml_use_internal_errors(true);
$doc->loadHTML($response);
libxml_clear_errors();
$xml = $doc->saveXML($doc->documentElement);
$xml = simplexml_load_string($xml);
Use this helped me in a big way.

simplexml_load_string not creating an object

My XML is:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<FindOrders xmlns="http://www.JOI.com/schemas/ViaSub.WMS/">
<orders>
<order>
<MarkForName />
<BatchOrderID />
<CreationDate>2013-08-09T17:41:00</CreationDate>
<EarliestShipDate />
<ShipCancelDate />
<PickupDate />
<Carrier>USPS</Carrier>
<BillingCode>Prepaid</BillingCode>
<TotWeight>0.00</TotWeight>
<TotCuFt>0.00</TotCuFt>
<TotPackages>1.0000</TotPackages>
<TotOrdQty>1.0000</TotOrdQty>
<TotLines>1.00</TotLines>
<Notes />
<OverAllocated />
<PickTicketPrintDate />
<ProcessDate />
<TrackingNumber />
<LoadNumber />
<BillOfLading />
<MasterBillOfLading />
<ASNSentDate />
<ConfirmASNSentDate />
<RememberRowInfo>398879:12:2:::0:False</RememberRowInfo>
</order>
</orders>
</FindOrders>
<totalOrders xmlns="http://www.JOI.com/schemas/ViaSub.WMS/">1</totalOrders>
</soap:Body>
</soap:Envelope>
When I do:
$a = simplexml_load_string($str);
print_r($a);
I get: SimpleXMLElement Object ( ) instead of an object with all of those parameters. Why is this?
You are missing the namespace declaration (php manual) for SOAP
$a = simplexml_load_string($str);
$a->registerXPathNamespace('soap', 'http://www.w3.org/2003/05/soap-envelope');
$result = $a->xpath('//soap:Body');
print_r($result);
Result (preview)
Array
(
[0] => SimpleXMLElement Object
(
[FindOrders] => SimpleXMLElement Object
(
[orders] => SimpleXMLElement Object
(
[order] => SimpleXMLElement Object
...
...
I'm guessing when you say you want to see an object with all those parameters, you're looking to output the xml document you just created.
Looking at the documentation at http://www.php.net/manual/en/simplexmlelement.asxml.php, here's what you need to do:
echo $a->asXML();
My case was dealing with soap response. I have to parse and clean it first before converting it to array.
$response = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $xml); // remove all colons and unnecessary characters.
$xml = new SimpleXMLElement($response);
$body = $xml->xpath('//sBody'); // should refer to soap body element like soapbody, sbody, etc..
$array = json_decode(json_encode((array)$body), TRUE);

SimpleXML SOAP response Namespace issues

After spending SEVERAL frustrated hours on this I am asking for your help.
I am trying to get the content of particular nodes from a SOAP response.
The response is
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"<xmlns:ns1="http://soap.xxxxxx.co.uk/">
<env:Body>
<ns1:PlaceOrderResponse>
<xxxxxOrderNumber></xxxxxOrderNumber>
<ErrorArray>
<Error>
<ErrorCode>24</ErrorCode>
<ErrorText>The+client+order+number+3002254+is+already+in+use</ErrorText>
</Error>
<Error>
<ErrorCode>1</ErrorCode>
<ErrorText>Aborting</ErrorText>
</Error>
</ErrorArray>
</ns1:PlaceOrderResponse>
</env:Body>
</env:Envelope>
I am trying to get at the nodes and children of <ErrorArray>.
Because of the XML containing namespaces
$XmlArray = new SimpleXMLElement($XmlStr);
foreach ($XmlArray->env:Envelope->env:Body->ns1:PlaceOrderResponse->ErrorArray->Error as $Error)
{
echo $Error->ErrorCode."<br />";
}
doesn't work.
I have read a number of articles such as
http://www.sitepoint.com/blogs/2005/10/20/simplexml-and-namespaces/
http://blog.stuartherbert.com/php/2007/01/07/using-simplexml-to-parse-rss-feeds/
and about 20 questions on this site, which unfortunately are not helping.
Even writing,
$XmlArray = new SimpleXMLElement($XmlStr);
echo "<br /><br /><pre>\n";
print_r($XmlArray);
echo "<pre><br /><br />\n";
gives
SimpleXMLElement Object
(
)
which makes me wonder if the soap response ($XmlStr) is actually a valid input for SimpleXMLElement.
It seems that the line
$XmlArray = new SimpleXMLElement($XmlStr);
is not doing what I expect it to.
Any help on how to get the nodes from the XML above would be very welcome.
Obviously getting it to work (having a working example) is what I need in the short term, but if someone could help me understand what I am doing wrong would be better in the long term.
Cheers.
Stu
You have to use SimpleXMLElement::children(), though at this point it would probably be easier to use XPath.
<?php
$XmlStr = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://soap.xxxxxx.co.uk/" >
<env:Body>
<ns1:PlaceOrderResponse>
<xxxxxOrderNumber></xxxxxOrderNumber>
<ErrorArray>
<Error>
<ErrorCode>24</ErrorCode>
<ErrorText>The+client+order+number+3002254+is+already+in+use</ErrorText>
</Error>
<Error>
<ErrorCode>1</ErrorCode>
<ErrorText>Aborting</ErrorText>
</Error>
</ErrorArray>
</ns1:PlaceOrderResponse>
</env:Body>
</env:Envelope>
XML;
$XmlArray = new SimpleXMLElement($XmlStr);
$t = $XmlArray->children("env", true)->Body->
children("ns1", true)->PlaceOrderResponse->
children()->ErrorArray->Error;
foreach ($t as $error) {
echo $error->ErrorCode, " " , $error->ErrorText, "<br />";
}
gives:
24 The+client+order+number+3002254+is+already+in+use
1 Aborting
A cheap and nasty way is to just simply remove the prefixes:
$xml = preg_replace('%<(\/)?(\S+):(\w+)%', '<$1$3', $xml);

Categories