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
How to Find a Reason When Mkdir Fails from PHP
How to Search Array for Multiple Values in PHP
Using Named Parameters with Pdo for Like
Laravel - Decryptexception: 'The MAC Is Invalid'
Sending/Displaying a Base64 Encoded Image
Auto Increment Skipping Numbers
How to Pass an Array of Pdo Parameters Yet Still Specify Their Types
Why Do I Need to Use a Popular Framework
Is MySQLi_Multi_Query Asynchronous
PHP Timezone Database Is Corrupt Error
Random Float Between 0 and 1 in PHP
PHP Session-Based Flash Message
Calling Perl Script from PHP and Passing in Variables, While Also Using Variablized Perl Script Name
Cascade Dropdown List Using Jquery/Php