Simplexml Soap Response Namespace Issues

SimpleXML SOAP response Namespace issues

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

PHP simpleXML: Handling Unknown Namespaces in SOAP Requests

First off, if you plan to use SOAP a lot, you may want to take a look at PHP's SOAP extension if you haven't already. I've never used it, though.

Back to your question, you said "In my case, I've seen inconsistent namespaces in SOAP requests." Get ready because I'm about to blow your mind: no you haven't. :)

In those three examples, the two namespaces are the same: there's http://schemas.xmlsoap.org/soap/envelope/ and there's http://developer.intuit.com/ -- What's different here is their prefix. The good news is the prefix doesn't really matter. See it as an alias to the namespace. The prefixes used in the document are automatically registered for use in XPath, but you can also register your own.

Here's an example of how to use the prefixes that were defined in the document (good if you already know what they are) or register your own prefixes and use those.

$xml = '<soapenv:Envelope 
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:dev="http://developer.intuit.com/">
<soapenv:Header/>
<soapenv:Body>
<dev:authenticate>
<dev:strUserName>username</dev:strUserName>
<dev:strPassword>password</dev:strPassword>
</dev:authenticate>
</soapenv:Body>
</soapenv:Envelope>';

$Envelope = simplexml_load_string($xml);

// you can register and use your own prefixes
$Envelope->registerXPathNamespace('soap', 'http://schemas.xmlsoap.org/soap/envelope/');
$Envelope->registerXPathNamespace('auth', 'http://developer.intuit.com/');

$nodes = $Envelope->xpath('/soap:Envelope/soap:Body/auth:authenticate/auth:strUserName');
$username = (string) $nodes[0];

// or you can use the prefixes that are already defined in the document
$nodes = $Envelope->xpath('/soapenv:Envelope/soapenv:Body/dev:authenticate/dev:strPassword');
$password = (string) $nodes[0];

var_dump($username, $password);

Simlpexml and registerxpathnamespace issue with soap response body

It's quite odd to see thats not working, but alternatively you could also use this:

$dom = new DOMDocument();
$dom->loadXML($xml);
$xpath = new DOMXpath($dom);
$element = $xpath->query('//soapenv:Body/ser:getAnalyticalDeliveryEstimatesRequest');

foreach($element->item(0)->childNodes as $node) {
// perform your actions here
}

Sample Output

Edit: Also this is another way:

$xml_string ='<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ser="http://www.example.com/v1/services">
<soapenv:Header/>
<soapenv:Body>
<ser:getAnalyticalDeliveryEstimatesRequest>
<ser:buyer>
<ser:buyerId>1233</ser:buyerId>
<ser:toCountry>IN</ser:toCountry>
<ser:toZip>110001</ser:toZip>
</ser:buyer>
<ser:item>
<ser:id>25164</ser:id>
<ser:categoryId>15032</ser:categoryId>
<ser:seller>
<ser:sellerId>11997</ser:sellerId>
<ser:fromCountry>IN</ser:fromCountry>
</ser:seller>
<ser:transactionId>0</ser:transactionId>
</ser:item>
</ser:getAnalyticalDeliveryEstimatesRequest>
</soapenv:Body>
</soapenv:Envelope>';
$xml = simplexml_load_string($xml_string, null, null, 'http://schemas.xmlsoap.org/soap/envelope/');
$ns = $xml->getNamespaces(true);
$soap = $xml->children($ns['soapenv']);
foreach($soap->Body as $nodes) {
$ser = $nodes->children($ns['ser'])->getAnalyticalDeliveryEstimatesRequest;
foreach($ser->buyer as $sub_nodes) { // this can also be ->item as well

}
}

simplexml_load_string() will not read soap response with soap: in the tags

A tag name with a colon in indicates the tag is in a non-default namespace. SimpleXML only looks at one namespace at a time, so you need to specifically select the namespace using the ->children() method.

In this case $xml->children('http://www.w3.org/2003/05/soap-envelope')->Body or $xml->children('soap', true)->Body should both work.

For this and other reasons, it's not advisable to use print_r to debug SimpleXML objects. Try this dedicated function instead.

Using xpath on a PHP SimpleXML object, SOAP + namespaces (not working..)

The multiple namespaces are messing with it, adding the following works for me

$response->registerXPathNamespace("site", "http://webservices.site.com/definitions");
$_res = $response->xpath('//site:SessionId');

also, see this previous stack overflow question

parse a SOAP XML response with Namespaces using PHP

The rate data can be accessed like this:

Demo

$obj = simplexml_load_string($xml);

foreach($obj->children('http://schemas.xmlsoap.org/soap/envelope/')->Body->children('urn:wsTest')->ServiceNameRQ->GetRatesRS->Rates->Rate as $rate)
{
echo (string)$rate->RateName . "\n";
}

Outputs

Rack Rate

Normal Rate

If you want the Rate attributes, you can get them like this (within the loop):

echo $rate->attributes()->RateID;

You can read the R1 and R2 elements like this:

foreach($obj->children('http://schemas.xmlsoap.org/soap/envelope/')->Body->children('urn:wsTest')->ServiceNameRQ->GetRatesRS->Rates->Rate as $rate)
{
if(isset($rate->R1))
{
echo $rate->R1->attributes()->Name;
echo $rate->R1->attributes()->MinRate;
}
if(isset($rate->R2))
{
echo $rate->R2->attributes()->Name;
echo $rate->R2->attributes()->MinRate;
}
}

SOAP response to XML with SimpleXML

A SOAP message is already XML. The problem is that it has namespaces so you have to access it differently. (The part before the colon is the identifier for the namespace.)

Here (google cached copy) is an example of using namespaces with SimpleXML.

Here is a specific example for reading SOAP messages.



Related Topics



Leave a reply



Submit