Feb 22

SimpleXML does not correctly parse SOAP XML results if the result comes back with colons ‘:’ in a tag, like <soap:Envelope>. Why? Because SimpleXML treats the colon character ‘:’ as an XML namespace, and places the entire contents of the SOAP XML result inside a namespace within the SimpleXML object. There is no real way to correct this using SimpleXML, but we can alter the raw XML result a little before we send it to SimpleXML to parse.

All we have to do is use the preg_replace function to get rid of the colons in the SOAP response tags BEFORE you hand it off to SimpleXML, like so:

// Send off SOAP request and get response back
$response = $soapClient->__doRequest($soapXml, $host, $soapActionXmlNS, "1.1");
 
// SimpleXML seems to have problems with the colon ":" in the <xxx:yyy> response tags, so take them out
$xmlString = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $response);
 
// Use SimpleXML to parse response into object
$xml = new SimpleXMLElement($xmlString);
 
// SimpleXML object for tags in the <soap:Body> node (just remove the colon like you did above)
$result = $xml->soapBody;

So in this example code, one of the response tags we got back from our SOAP request was <soap:Body>. After our preg_replace found and removed the first colon inside the tags, our <soap:Body> tag is now <soapBody>. So to access this tag in SimpleXML, we simply have to remember to remove the colon so it becomes $xml->soapBody instead of attempting to do $xml->{”soap:Body”} (which will fail).

So putting it all together, the entire process would look something like the following:

<?php
$host = "http://www.example.com/soapserver.php";
$soapActionXmlNS = "http://tempuri.org/";
 
// Establish new SOAP client
$soapClient = new SoapClient(NULL,
	array(
	"location" => $host,
	"uri"      => "http://schemas.xmlsoap.org/soap/envelope/",
	"style"    => SOAP_RPC,
	"use"      => SOAP_ENCODED
	));
 
// Build SOAP XML request
$soapXml = '
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<UpdateSomeField xmlns="' . $soapActionXmlNS . '">
	<Username>SomeUser</Username>
	<Password>SomePass</Password>
	<SomeField>This is the new data</SomeField>
</UpdateSomeField>
</soap:Body>
</soap:Envelope>
';
 
// Send off SOAP request and get response back
$response = $soapClient->__doRequest($soapXml, $host, $soapActionXmlNS, "1.1");
 
// SimpleXML seems to have problems with the colon ":" in the <xxx:yyy> response tags, so take them out
$xmlString = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $response);
 
// Use SimpleXML to parse response into object
$xml = new SimpleXMLElement($xmlString);
 
// SimpleXML object for tags in the <soap:Body> node
$result = $xml->soapBody;
 
// You can now use $result to access whatever response tags you need to, just like normal
echo $result->SomeFieldInRespose;
?>

The code provided above will only find and remove the FIRST colon found in the tag BEFORE any whitespace, so all other valid namespaces will remain intact. So if you get a tag with a valid namespace like this one: ‘<RegistrationResponse xmlns=”urn:Microsoft.Search”>’ it will remain intact and function like it is supposed to.

Note that this example uses PHP’s SOAP extension. If your server does not support the SOAP extension, there are other SOAP libraries in native PHP code available to you like NuSOAP.


2 comments so far...

  • Omid Said on October 3rd, 2008 at 9:38 am:

    Very nice hack!
    I have been struggling with the PHP SOAP package and realized it has a bug so I decided to create my own SOAP client with simpleXML and cURL. Your approach to overcome the simpleXML’s shortcoming in handling of namespaces saved me a lot of time…

  • Ron Said on October 24th, 2008 at 6:37 pm:

    Thank you, thank you, thank you for this! It solved a HUGE headache I was dealing with.

leave a reply