ChatGPT解决这个技术问题 Extra ChatGPT

How to parse SOAP XML?

SOAP XML:

<?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>
    <PaymentNotification xmlns="http://apilistener.envoyservices.com">
      <payment>
        <uniqueReference>ESDEUR11039872</uniqueReference>      
        <epacsReference>74348dc0-cbf0-df11-b725-001ec9e61285</epacsReference>
        <postingDate>2010-11-15T15:19:45</postingDate>
        <bankCurrency>EUR</bankCurrency>
        <bankAmount>1.00</bankAmount>
        <appliedCurrency>EUR</appliedCurrency>
        <appliedAmount>1.00</appliedAmount>
        <countryCode>ES</countryCode>
        <bankInformation>Sean Wood</bankInformation>
  <merchantReference>ESDEUR11039872</merchantReference>
   </payment>
    </PaymentNotification>
  </soap:Body>
</soap:Envelope>

How to get 'payment' element?

I try to parse (PHP)

$xml = simplexml_load_string($soap_response);
$xml->registerXPathNamespace('soap', 'http://schemas.xmlsoap.org/soap/envelope/');
foreach ($xml->xpath('//payment') as $item)
{
    print_r($item);
}

Result is empty :( Any ideas how to parse it correct?

welcome to SO. next time, click the format button at the top of the textarea so the code is easier to read.
@stillstanding: Ooh, apparently I overwrote your edit just as you submitted it :)
As you are fairly new here, I recommend you to read stackoverflow.com/faq#howtoask and then accept my answer, as the most relevant ;-)

A
Aran

One of the simplest ways to handle namespace prefixes is simply to strip them from the XML response before passing it through to simplexml such as below:

$your_xml_response = '<Your XML here>';
$clean_xml = str_ireplace(['SOAP-ENV:', 'SOAP:'], '', $your_xml_response);
$xml = simplexml_load_string($clean_xml);

This would return the following:

SimpleXMLElement Object
(
    [Body] => SimpleXMLElement Object
        (
            [PaymentNotification] => SimpleXMLElement Object
                (
                    [payment] => SimpleXMLElement Object
                        (
                            [uniqueReference] => ESDEUR11039872
                            [epacsReference] => 74348dc0-cbf0-df11-b725-001ec9e61285
                            [postingDate] => 2010-11-15T15:19:45
                            [bankCurrency] => EUR
                            [bankAmount] => 1.00
                            [appliedCurrency] => EUR
                            [appliedAmount] => 1.00
                            [countryCode] => ES
                            [bankInformation] => Sean Wood
                            [merchantReference] => ESDEUR11039872
                        )

                )

        )

)

Namespaces are usually misleading and removing it seems to be the fastest (hack) way to restore proper xml object. thx
awesome solution. soap sucks.
In my case I had to add a couple more array values to str_ireplace but in the end this was the only working solution I found. Upvoted. Also agreed with @JohnBallinger soap sucks.
Thanks! this save me a lot of time. Just replace namespaces with str_replace and a simple xML object will work.
Added some values to the str_ireplace works for me :) ['SOAP-ENV:', 'env:', 'SOAP:', 'ns1:']
I
Ivan

PHP version > 5.0 has a nice SoapClient integrated. Which doesn't require to parse response xml. Here's a quick example

$client = new SoapClient("http://path.to/wsdl?WSDL");
$res = $client->SoapFunction(array('param1'=>'value','param2'=>'value'));
echo $res->PaymentNotification->payment;

What is SoapFunction here?
@Edward it's any function(operation) defined in the SOAP service's WSDL you want to access
@Ivan So it's one of those services listed in the XML on the asmx server? By the way, you think you could take a look at my question related to this? stackoverflow.com/questions/17042984/…
@Edvard yes. SOAP server will define what kind of operations it can perform and what parameters are needed for each operation. so in the example above. "Soap Function" is a name of an operation as defined by Soap server. Parameters go in an array
N
Neeme Praks

In your code you are querying for the payment element in default namespace, but in the XML response it is declared as in http://apilistener.envoyservices.com namespace.

So, you are missing a namespace declaration:

$xml->registerXPathNamespace('envoy', 'http://apilistener.envoyservices.com');

Now you can use the envoy namespace prefix in your xpath query:

xpath('//envoy:payment')

The full code would be:

$xml = simplexml_load_string($soap_response);
$xml->registerXPathNamespace('envoy', 'http://apilistener.envoyservices.com');
foreach ($xml->xpath('//envoy:payment') as $item)
{
    print_r($item);
}

Note: I removed the soap namespace declaration as you do not seem to be using it (it is only useful if you would use the namespace prefix in you xpath queries).


But why is the namespace ‘envoy’? Why not ‘soap’?
XML document in the question has elements with namespace URI "apilistener.envoyservices.com" and the question is about how to query for elements in that namespace. You do not have to call the prefix "envoy" (can be whatever unique in the scope of XPath query) but "envoy" seemed like a logical name, based on the namespace URI. I would not call it "soap" as that should be reserved for "schemas.xmlsoap.org/soap/envelope" namespace.
B
Bảo Nam
$xml = '<?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>
                    <PaymentNotification xmlns="http://apilistener.envoyservices.com">
                      <payment>
                        <uniqueReference>ESDEUR11039872</uniqueReference>
                        <epacsReference>74348dc0-cbf0-df11-b725-001ec9e61285</epacsReference>
                        <postingDate>2010-11-15T15:19:45</postingDate>
                        <bankCurrency>EUR</bankCurrency>
                        <bankAmount>1.00</bankAmount>
                        <appliedCurrency>EUR</appliedCurrency>
                        <appliedAmount>1.00</appliedAmount>
                        <countryCode>ES</countryCode>
                        <bankInformation>Sean Wood</bankInformation>
                  <merchantReference>ESDEUR11039872</merchantReference>
                   </payment>
                    </PaymentNotification>
                  </soap:Body>
                </soap:Envelope>';
        $doc = new DOMDocument();
        $doc->loadXML($xml);
        echo $doc->getElementsByTagName('postingDate')->item(0)->nodeValue;
        die;

Result is:

2010-11-15T15:19:45

This would work, but it is not technically correct. You are considering SOAP XML response to be a DOM Document, which it is not.
This works but only with static xml passed to the "$xml" variable. Return empty result when passed SOAP Response to it.
Good solution for handling simple response with no available WSDL.
U
UTKARSH SINGHAL

First, we need to filter the XML so as to parse that into an object

$response = strtr($xml_string, ['</soap:' => '</', '<soap:' => '<']);
$output = json_decode(json_encode(simplexml_load_string($response)));
var_dump($output->Body->PaymentNotification->payment);

o
omarjebari

This is also quite nice if you subsequently need to resolve any objects into arrays: $array = json_decode(json_encode($responseXmlObject), true);


D
David Kurniawan

First, we need to filter the XML so as to parse that change objects become array

//catch xml
$xmlElement = file_get_contents ('php://input');
//change become array
$Data = (array)simplexml_load_string($xmlElement);
//and see
print_r($Data);

a
almightyBob

why don't u try using an absolute xPath

//soap:Envelope[1]/soap:Body[1]/PaymentNotification[1]/payment

or since u know that it is a payment and payment doesn't have any attributes just select directly from payment

//soap:Envelope[1]/soap:Body[1]/PaymentNotification[1]/payment/*

foreach ($xml->xpath('//soap:Envelope[1]/soap:Body[1]/PaymentNotification[1]/payment/*') as $item) { print_r($item); } also empty (
try giving your second namespace an actual value. and define that then.
could you give example please
you can see that approach in my response ;-)
result is the same -> foreach ($xml->xpath('//soap:Envelope[1]/soap:Body[1]/PaymentNotification[1]/payment/*') as $item){print_r($item);}

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now