simplexml_load_string return empty result - php

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>"

Related

simplexml_load_string always fails if there are special characters inside tags

I'm receiving from an external web service an XML like this(notice the colons inside the tags)
XML
<?xml version='1.0' encoding='UTF-8'?>
<env:Envelope
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns0="http://webservice/CrsWSApi.wsdl/types/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
<ns0:callwsapiResponseElement>
<ns0:result>
<ns0:object>BTS</ns0:object>
<ns0:receiver>BTSV45_ANSF_PROD-AKITT</ns0:receiver>
<ns0:sender>ANSFP</ns0:sender>
<ns0:data>
{"name":"Paolo", "cognome": Rossi, "data":"18/11/1983"}
</ns0:data>
<ns0:errormess xsi:nil="true"/>
<ns0:errorcode xsi:nil="true"/>
<ns0:status>1</ns0:status>
<ns0:sessionid>akit1</ns0:sessionid>
</ns0:result>
</ns0:callwsapiResponseElement>
</env:Body>
</env:Envelope>
if I use this function to parse it
PHP
$xml = simplexml_load_string($xml) or die("Error: Cannot create object");
I'm always receiving Error. If I remove the colons it works as expected. Is there a way to make this works without manipulating the colons before the xml parsing?
I want to avoid this manipulation
PHP
$xml = preg_replace(['/env:/','/:env(?!=)/','/ns0:/','/:ns0(?!=)/'], '', $myXMLData);
UPDATE
I already accepted a good answer that is reporting usefull links on how namespases are working in an xml environment, but I want to share another way to obtain the same result without parsing an xpath expression. We can you use the children method of simple_xml_loadstring specifiying the second parameter as a namespace.
fot the above code you can obtain the ns0:data content with this code(before you have to change the if statement as the accepted answer)
PHP
$xml = simplexml_load_string($response);
echo $xml
->children("env", true)
->Body
->children("ns0", true)
->callwsapiResponseElement
->result
->data;
// This will print
{"name":"Paolo", "cognome": Rossi, "data":"18/11/1983"}
For me it is very curious the return of simplexml_load_string (https://www.php.net/manual/en/function.simplexml-load-string.php):
This function may return Boolean false, but may also return a non-Boolean value which evaluates to false. Please read the section on Booleans for more information. Use the === operator for testing the return value of this function.
And indeed if you replace the or die:
<?php
$response = <<<XML
<?xml version='1.0' encoding='UTF-8'?>
<env:Envelope
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns0="http://webservice/CrsWSApi.wsdl/types/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
<ns0:callwsapiResponseElement>
<ns0:result>
<ns0:object>BTS</ns0:object>
<ns0:receiver>BTSV45_ANSF_PROD-AKITT</ns0:receiver>
<ns0:sender>ANSFP</ns0:sender>
<ns0:data>
{"name":"Paolo", "cognome": Rossi, "data":"18/11/1983"}
</ns0:data>
<ns0:errormess xsi:nil="true"/>
<ns0:errorcode xsi:nil="true"/>
<ns0:status>1</ns0:status>
<ns0:sessionid>akit1</ns0:sessionid>
</ns0:result>
</ns0:callwsapiResponseElement>
</env:Body>
</env:Envelope>
XML;
$xml = simplexml_load_string($response);
if($xml===false){
die();
}
echo $xml->xpath("//*[local-name() = 'receiver']")[0];
Results:
BTSV45_ANSF_PROD-AKITT
You can then use the namespaces to find your data. This is a nice post Reference - How do I handle Namespaces (Tags and Attributes with a Colon in their Name) in SimpleXML?

Parse XML data from PHP SOAP Response

I have successfully been able to get a response from an API im working with, with the help of Brian Driscoll but am having trouble parsing that XML data. Here is the script to get the response and the returned XML data -
$clientC = new SoapClient('http://webservice.nada.com/vehicles/vehicle.asmx?wsdl',array('trace' => 1,'exceptions' => 1, 'cache_wsdl' => 0));
$params = new stdClass();
$params->Token = $token;
$params->Period = 1;
$params->VehicleType = "UsedCar";
$params->Vin = '5YFBURHE3FP331896';
$params->Region = 10;
$params->Mileage = 100;
$result = $clientC->getDefaultVehicleAndValueByVin(array('vehicleRequest' => $params));
$xml = htmlspecialchars($clientC->__getLastResponse());
This returns -
<?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>
<getDefaultVehicleAndValueByVinResponse xmlns="http://webservice.nada.com/">
<getDefaultVehicleAndValueByVinResult>
<Uid>1182699</Uid>
<VehicleYear>2015</VehicleYear>
<MakeCode>47</MakeCode>
</getDefaultVehicleAndValueByVinResult>
</getDefaultVehicleAndValueByVinResponse>
</soap:Body>
</soap:Envelope>
I would like to either parse the XML and retrieve something like
$vehicle_year = $xml->VehicleYear;
I have tried -
$xml = htmlspecialchars($clientC->__getLastResponse());
$response = strtr($xml, ['</soap:' => '</', '<soap:' => '<']);
$output = json_decode(json_encode(simplexml_load_string($response)));
var_dump($output->Body->getHighVehicleAndValueByVinResponse->VehicleYear);
But returns NULL
This is the usual problem of having various namespaces which you need to navigate around. The root node defines xmlns:soap so you can use that without having to do anything, so the XPath uses //soap:Body/* to find the element inside the body tag, as xpath() returns a list of matching nodes, use [0] to just pick the only one out.
As the body data is all under a default namespace (defined as xmlns="http://webservice.nada.com/") you can extract all of them using $data->children("http://webservice.nada.com/"). This now allows you to use standard object notation to access the values.
One thing to note is although echo automatically converts it to a string, if you use these values elsewhere - you may need to convert it using (string) as the item is in fact a SimpleXMLElement.
$data = <<< XML
<?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>
<getDefaultVehicleAndValueByVinResponse xmlns="http://webservice.nada.com/">
<getDefaultVehicleAndValueByVinResult>
<Uid>1182699</Uid>
<VehicleYear>2015</VehicleYear>
<MakeCode>47</MakeCode>
</getDefaultVehicleAndValueByVinResult>
</getDefaultVehicleAndValueByVinResponse>
</soap:Body>
</soap:Envelope>
XML;
$xml = simplexml_load_string($data);
$data = $xml->xpath("//soap:Body/*")[0];
$details = $data->children("http://webservice.nada.com/");
echo (string)$details->getDefaultVehicleAndValueByVinResult->VehicleYear;

SOAP response to PHP Array, reaching node Attributes

I'm trying to convert the following response into an array in PHP, I want to reach the data inside 'Timbre', I need the values of:
UUID
FechaTimbrado
SelloSAT
The solutions I've tried around here convert the nodes ok, but I can't find a way to reach nothing beyond 'Timbre', hope you can help me find a way to do this, here's the response I'm receiving:
<?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:Header>
<ResponseHeader trackId="119588" RequestDateTime="2017-11-09T11:55:39.0205969-06:00" IPCaller="187.190.165.93" ClienteId="0" Estatus="1" ID="91a407fe-c1c2-40af-bc2d-5aee58606161" Detail="" Ambiente="DESARROLLO-01" xmlns="https://cfdi.timbrado.com.mx/timbradov2">
<CFDI Id="CFDI33" FechaHora="2017-11-09T11:45:30" RFCEmisor="XXXHHK2343434" RFCReceptor="GFGDS45&667" Serie="G" Folio="35010" />
</ResponseHeader>
</soap:Header>
<soap:Body>
<GeneraTimbreResponse xmlns="https://cfdi.timbrado.com.mx/timbradov2">
<GeneraTimbreResult>
<Timbre>
<tfd:TimbreFiscalDigital xmlns:tfd="http://www.sat.gob.mx/TimbreFiscalDigital" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/cfd/TimbreFiscalDigital/TimbreFiscalDigitalv11.xsd" Version="1.1" UUID="91A407FE-C1C2-40AF-BC2D-5AEE58606161" FechaTimbrado="2017-11-09T11:55:39" RfcProvCertif="ASE0209252Q1" Leyenda="Timbre de prueba" SelloCFD="WCldH0Oan7h2VK3MuMM3Nv8HAg/OlCaKF2VLz/1u81yvZItgZuYia1Aaz27hOYsqBZ/m5/OhAaL6TZ43MZSF6zK9tZz+Fgu9wS2QJ+ubk/83oIjQDwLHCuJL/l5VRZI29RoPUfs3VDb+tD9mqeEinQwfLJG1YkoCIWC/4oXxL1oX5DEXqMW/sK52jpJh7exyqw+GzBA+LUBaSi+aGHlown8cEQmZOZieeUW5uXjDPQBdJBZ/XkRgfqzAiS/Tfj9a61B0Nrm7JyejagPZVW7E3gbcmFunWTbGwNEYvqWkgtnO7aoTzrVcjK3YX4t2zKPt8F7BGq+MSL/fge174IttGQ==" NoCertificadoSAT="20001000000300022323" SelloSAT="NhzPGgUh+1vQJkidObRpp+IgjSdOm6wwgMVVcPdEVmVEbFgCBW42a7grzg8toe9d/ZlHhka5g6h63E5jp3xKcq2KnWw+dQbrbh8xDNHC+7tPXEJN9T5JU8ZifpvTIu9g89TYERUs/4sLgAYtqAmV9AIQK17LoAIiNRdUzwWiiHUCLaqL7k0bmJYvfeuYAnkw52LfyxzLDg6TyodT/1LjJVwhGLyaOcOwCDObHH8ukZj0d2aOwOEE0IJBbbIgh3VaIp0/EvvFn1I1F9BnrLHVZFEcR3ZeP/TyLJmuOtxLGDnPtnaVQTrt0sQb43bG2R2ut5Bt3uS12xOMx3IfDr/3FQ==" />
</Timbre>
</GeneraTimbreResult>
</GeneraTimbreResponse>
</soap:Body>
</soap:Envelope>
So I solved it like this, if anyone knows a 'classier' way , let me know
//here I read the result string I posted above
$soap = simplexml_load_string(html_entity_decode($this->resultado_timbre));
//I read the nodes until I reach the 'Timbre' one
$response = $soap->children('http://www.w3.org/2003/05/soap-envelope')->Body->children()->children()->children()->Timbre->children('tfd',TRUE);
//The attributes are readable in a nice array
$atributos=$response->attributes();
Hope it helps someone!

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.

Categories