36

I am unable to convert SOAP response to Array in php.

here is the code

 $response = $client->__doRequest($xmlRequest,$location,$action,1);

here is the SOAP response.

<soap:envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:body>
<searchflightavailability33response xmlns="http://FpwebBox.Fareportal.com/Gateway.asmx">
<searchflightavailability33result>
    &lt;Fareportal&gt;&lt;FpSearch_AirLowFaresRS&gt;&lt;CntKey&gt;1777f5a7-7824-46ce-a0f8-33d5e6e96816&lt;/CntKey&gt;&lt;Currency CurrencyCode="USD"/&gt;&lt;OriginDestinationOptions&gt;&lt;OutBoundOptions&gt;&lt;OutBoundOption segmentid="9W7008V21Feb14"&gt;&lt;FlightSegment etc....
    </searchflightavailability33result>
</searchflightavailability33response>
</soap:body>
</soap:envelope>;

i used the following ways to convert to Array,but i am getting empty output.

1.echo '<pre>';print_r($client__getLastResponse());
2.echo '<pre>';print_r($response->envelope->body->searchflightavailability33response);
3.echo '<pre>';print_r($client->SearchFlightAvailability33($response));
     4.simplexml_load_string($response,NULL,NULL,"http://schemas.xmlsoap.org/soap/envelope/");  

5.echo '<pre>';print_r($client->SearchFlightAvailability33($response));

please advice me.

raheem.unr
  • 797
  • 1
  • 7
  • 16
  • Why you don't use the php SoapClient class (or even better the ZendSoap component) ? – lilobase Feb 14 '14 at 10:49
  • i first used SOAP class only like $client->__getLastResponse(),$client->SearchFlightAvailability33Response(); but nothing return. – raheem.unr Feb 14 '14 at 11:30
  • The proper use is : $yourData = $client->SearchFlightAvailability33(); – lilobase Feb 14 '14 at 11:32
  • hi i tried which you given suggestion but i got error only. try{ $response = $client->__doRequest($xmlRequest,$location,$action,1); //$response = $client->SearchFlightAvailability33($xmlRequest); //response = $client->__getLastResponse(); //$result = $client->SearchFlightAvailability33($xmlRequest); echo '
    ';print_r($client->SearchFlightAvailability33($res));                           
            }catch (Exception $e){
                echo '
    ';print_r($e->getMesage());
            }
    – raheem.unr Feb 14 '14 at 12:24
  • 1
    @lilobase, Sometimes, people are forced to use the "improper" methods because an API developer doesn't follow standard practices. I just ran into this and had to extend the SoapClient class in order to send my own custom XML. This isn't the "wrong" way, it's just not the normal way. – RattleyCooper Apr 03 '15 at 14:59

8 Answers8

61

The following SOAP response structure can be easily converted in an array using a combination of the previous methods. Using only the the function "simplexml_load_string" removing the colon ":" it returned null in some cases.

SOAP Response

<S:Envelope
    xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Body>
        <ns2:transaccionResponse
            xmlns:ns2="http://ws.iatai.com/">
            <respuestaTransaccion>
                <idTransaccion>94567</idTransaccion>
                <referencia>3958</referencia>
                <idEstado>3</idEstado>
                <nombreEstado>Declinada</nombreEstado>
                <codigoRespuesta>202</codigoRespuesta>
                <valor>93815.0</valor>
                <iva>86815.0</iva>
                <baseDevolucion>0.0</baseDevolucion>
                <isoMoneda>COP</isoMoneda>
                <fechaProcesamiento>24-07-2015 12:18:40 PM</fechaProcesamiento>
                <mensaje>REJECT</mensaje>
                <tarjetaRespuesta>
                    <idFranquicia>1</idFranquicia>
                    <nombreFranquicia>VISA</nombreFranquicia>
                    <numeroBin>411111</numeroBin>
                    <numeroProducto>1111</numeroProducto>
                </tarjetaRespuesta>
                <procesadorRespuesta>
                    <idProcesador>3</idProcesador>
                </procesadorRespuesta>
            </respuestaTransaccion>
        </ns2:transaccionResponse>
    </S:Body>
</S:Envelope>

PHP conversion:

$response = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $response);
$xml = new SimpleXMLElement($response);
$body = $xml->xpath('//SBody')[0];
$array = json_decode(json_encode((array)$body), TRUE); 
print_r($array);

Result:

Array
(
    [ns2transaccionResponse] => Array
        (
            [respuestaTransaccion] => Array
                (
                    [idTransaccion] => 94567
                    [referencia] => 3958
                    [idEstado] => 3
                    [nombreEstado] => Declinada
                    [codigoRespuesta] => 202
                    [valor] => 93815.0
                    [iva] => 86815.0
                    [baseDevolucion] => 0.0
                    [isoMoneda] => COP
                    [fechaProcesamiento] => 24-07-2015 12:18:40 PM
                    [mensaje] => REJECT
                    [tarjetaRespuesta] => Array
                        (
                            [idFranquicia] => 1
                            [nombreFranquicia] => VISA
                            [numeroBin] => 411111
                            [numeroProducto] => 1111
                        )

                    [procesadorRespuesta] => Array
                        (
                            [idProcesador] => 3
                        )

                )

        )

)
neurona.dev
  • 1,347
  • 1
  • 14
  • 12
  • 1
    This is a much more elegant solution than the excepted answer, and worked perfectly. Thank you. The only pointer I have, is if you want the whole request instead of just the body, don't use: ```$xml->xpath(``` just do: ```json_encode($xml)``` If you do use ```$xml->xpath(``` remember to use whatever key your body is sent in, mine was ```soapBody``` instead of ```SBody``` – Casper Wilkes Feb 22 '16 at 14:58
  • also, maybe: `$array = json_decode( str_replace('@', '', json_encode((array)$body)), TRUE); ` – Patrioticcow Feb 13 '17 at 18:13
  • Seems that it can be simplified further: – RedScourge Dec 23 '20 at 01:08
  • 2
    `$response = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $response); $xml = new SimpleXMLElement($response); $array = json_decode( str_replace('@', '', json_encode($xml)), TRUE); print_r($array);` – RedScourge Dec 23 '20 at 01:08
  • I got SOAP response in CURL request but not able to convert in to XML. Show object(SimpleXMLElement)#6 (0) { } – Krunal Pandya Jun 30 '21 at 06:54
51

I found a perfect solution to parse SOAP response to an Array:

$plainXML = mungXML( trim($soapXML) );
$arrayResult = json_decode(json_encode(SimpleXML_Load_String($plainXML, 'SimpleXMLElement', LIBXML_NOCDATA)), true);

print_r($arrayResult);


// FUNCTION TO MUNG THE XML SO WE DO NOT HAVE TO DEAL WITH NAMESPACE
function mungXML($xml)
{
    $obj = SimpleXML_Load_String($xml);
    if ($obj === FALSE) return $xml;

    // GET NAMESPACES, IF ANY
    $nss = $obj->getNamespaces(TRUE);
    if (empty($nss)) return $xml;

    // CHANGE ns: INTO ns_
    $nsm = array_keys($nss);
    foreach ($nsm as $key)
    {
        // A REGULAR EXPRESSION TO MUNG THE XML
        $rgx
        = '#'               // REGEX DELIMITER
        . '('               // GROUP PATTERN 1
        . '\<'              // LOCATE A LEFT WICKET
        . '/?'              // MAYBE FOLLOWED BY A SLASH
        . preg_quote($key)  // THE NAMESPACE
        . ')'               // END GROUP PATTERN
        . '('               // GROUP PATTERN 2
        . ':{1}'            // A COLON (EXACTLY ONE)
        . ')'               // END GROUP PATTERN
        . '#'               // REGEX DELIMITER
        ;
        // INSERT THE UNDERSCORE INTO THE TAG NAME
        $rep
        = '$1'          // BACKREFERENCE TO GROUP 1
        . '_'           // LITERAL UNDERSCORE IN PLACE OF GROUP 2
        ;
        // PERFORM THE REPLACEMENT
        $xml =  preg_replace($rgx, $rep, $xml);
    }

    return $xml;

} // End :: mungXML()

It will give you a perfect array of SOAP XML Data.

Irshad Khan
  • 5,670
  • 2
  • 44
  • 39
  • 6
    awesome, worked first time, where many attempts had failed. Thanks for sharing your hard work! – Simon Unsworth Apr 21 '20 at 15:27
  • Mna, you are a legend. I have no clue why this hasn't been accepted answer - works like a charm, where everything else I tried failed. – Kalko Jul 28 '22 at 14:26
  • This is excellent thanks @irshad-khan - can I ask how to go about not having the namespace in resulting XML at all instead of having it with the underscore and tag name? I'm struggling with the regular expression. So instead of 123 I am looking for 123. – SammyBlackBaron Aug 19 '22 at 13:19
17

finally i found the solution is

we can get body of the response from SOAP the following ways

example1:

$xml = new SimpleXMLElement($soapResponse);
foreach($xml->xpath('//soap:body') as $header) {
    $output = $header->registerXPathNamespace('default', 'http://FpwebBox.Fareportal.com/Gateway.asmx');    
}

example2:

$doc = new DOMDocument('1.0', 'utf-8');
    $doc->loadXML( $soapResponse );
    $XMLresults     = $doc->getElementsByTagName("SearchFlightAvailability33Response");
    $output = $XMLresults->item(0)->nodeValue;
raheem.unr
  • 797
  • 1
  • 7
  • 16
  • 1
    Both of the solutions didn't work for me can you please see my [question](https://stackoverflow.com/questions/55980022/unable-to-convert-soap-response-string-to-xml-in-yii2) related to it ? – Moeez May 04 '19 at 12:11
  • please explain the arguments of registerXPathNamespace – TomTerrific May 15 '19 at 19:22
  • Example 2 is best solution as dealing with namespaces is bit challenging as namespace prefixes are not reliable when receiving data from 3rd Party services. Best approach is to getElementsByTagName and retrieve specific values and create an Array of all data element which are required either to be validated or further displayed on the view layer. All other approaches mentioning stripping fo namespaces & prefixes using regular expressions are not optimal and may produce incorrect results in different environment for the same SOAP API call. – Shoaib Khan Jun 24 '19 at 16:40
  • Actually Example 2 ignores the namespace URI but depends on the fact that the tag has no prefix. A better approach would be `$doc->getElementsByTagNameNS('http://FpwebBox.Fareportal.com/Gateway.asmx', "SearchFlightAvailability33Response")` – ThW Nov 01 '19 at 16:44
9

Php parse SOAP response to an array

 $xml = file_get_contents($response);

// SimpleXML seems to have problems with the colon ":" in the <xxx:yyy> response tags, so take them out
$xml = preg_replace(“/(<\/?)(\w+):([^>]*>)/”, “$1$2$3″, $xml);
$xml = simplexml_load_string($xml);
$json = json_encode($xml);
$responseArray = json_decode($json,true);
Amuk Saxena
  • 1,521
  • 4
  • 18
  • 44
2

I got a single value using DOMDocument.

    $soap_response = $client->__getLastResponse();

    $dom_result = new DOMDocument;
    if (!$dom_result->loadXML($soap_response))
        throw new Exception(_('Error parsing response'), 11);

    $my_val = $dom_result->getElementsByTagName('my_node')->item(0)->nodeValue;
TobiMcNamobi
  • 4,687
  • 3
  • 33
  • 52
2

The second answer from @gtrujillos worked for me but with some changes, because the json string needs more formatting code in my case and the xPath needs to be in this way "//S:Body". This is the code that finally solve my problem. I toke the same SOAP Response example and the code for getting and returning a Soap response I found it here:

PHP convertion

//getting the Soap Response
header("Content-Type: text/xml\r\n");
ob_start();

$capturedData = fopen('php://input', 'rb');
$content = fread($capturedData, 5000);
fclose($capturedData);
ob_end_clean();

//getting the SimpleXMLElement object
$xml = new SimpleXMLElement($content);
$body = $xml->xpath('//S:Body')[0];

//transform to json
$json = json_encode((array)$body);

//Formatting the JSON
$json = str_replace(array("\n","\r","?"),"",$Json);
$json = preg_replace('/([{,]+)(\s*)([^"]+?)\s*:/','$1"$3":',$json);
$Json = preg_replace('/(,)\s*}$/','}',$json);

//Getting the array
$array=json_decode($Json,true);

//whatever yo need to do with the array ...

//Return a Soap Response
$returnedMsg=true;//this will depend of what do you need to return
print '<?xml version="1.0" encoding="UTF-8"?>
     <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <soapenv:Body>
     <notifications xmlns="http://soap.sforce.com/2005/09/outbound">
     <Ack>' . $returnedMsg . '</Ack>
     </notifications>
     </soapenv:Body>
     </soapenv:Envelope>';
0

SOAP can be read as just XML but you should take the namespaces into consideration. This is not difficult with PHPs DOM. In your example the SOAP uses a namespace for the inner nodes and the {http://FpwebBox.Fareportal.com/Gateway.asmx}searchflightavailability33result element contains another XML document as text content.

$xml = <<<'XML'
<soap:envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:body>
<searchflightavailability33response xmlns="http://FpwebBox.Fareportal.com/Gateway.asmx">
<searchflightavailability33result>
    &lt;Fareportal&gt;&lt;FpSearch_AirLowFaresRS&gt;&lt;CntKey&gt;1777f5a7-7824-46ce-a0f8-33d5e6e96816&lt;/CntKey&gt;&lt;Currency CurrencyCode="USD"/&gt;&lt;/FpSearch_AirLowFaresRS&gt;&lt;/Fareportal&gt;
    </searchflightavailability33result>
</searchflightavailability33response>
</soap:body>
</soap:envelope>
XML;

// define a constant for the namespace URI
const XMLNS_FPW = 'http://FpwebBox.Fareportal.com/Gateway.asmx';

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
// register a prefix for the namespace
$xpath->registerNamespace('fpw', XMLNS_FPW);

// read the result element text content
$result = $xpath->evaluate('string(//fpw:searchflightavailability33result)');
var_dump($result);

// load the result string as XML
$innerDocument = new DOMDocument();
$innerDocument->loadXML($result);
$innerXpath = new DOMXpath($innerDocument);

// read data from it
var_dump($innerXpath->evaluate('string(//CntKey)'));
ThW
  • 19,120
  • 3
  • 22
  • 44
0

in my case i used $body = $xml->xpath('//soapBody')[0];

$response = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $response);
$xml = new SimpleXMLElement($response);
$body = $xml->xpath('//SBody')[0];
$array = json_decode(json_encode((array)$body), TRUE);
print_r($array);
Zain Ali
  • 1
  • 2