Verwerken van XML response vanuit SOAP request
Als basic programmeur ben ik al 2 dagen bezig om een groot probleem op te lossen die waarschijnlijk heel simpel is voor de programmeur experts onder ons. Het volgende is er aan de hand.
Via onderstaand stukje script roep ik de status op van een bepaalde zending:
Code (php)
1
2
3
2
3
$WebserviceClient = new SoapClient("http://xxx/App_Services/xxx.asmx?wsdl");
$parameters= array("Username"=>$username, "Password"=>$password, "Zending"=>$zending, "Postcode"=>$postocde, "Referentie"=>$referentie);
$WebmethodResult = $WebserviceClient->GetShipmentStatus($parameters) or die('FOUT!');
$parameters= array("Username"=>$username, "Password"=>$password, "Zending"=>$zending, "Postcode"=>$postocde, "Referentie"=>$referentie);
$WebmethodResult = $WebserviceClient->GetShipmentStatus($parameters) or die('FOUT!');
Als ik vervolgens via PRINT_R het resultaat in $WebmethodResult op het scherm toon krijg ik een response terug die begint met stdClass Object en daarna alle informatie over die bepaalde zending.
mijn print_r ziet er als volgt uit:
Als ik de BRONCODE van de code op het scherm bekijk zie ik veel code staan zoals
<MovementID>225362222</MovementID><ShipmentID>18815421</ShipmentID><CompanyID>985</CompanyID><MovementType>Afgeleverd</MovementType>
Mijn vraag is hoe ik met PHP de response ($WebmethodResult) uit de SOAP request kan benaderen zodat ik het MovementType element kan declareren in een variable waarna ik een if/else constructie kan maken zoals:
Ik hoop dat ik het probleem goed geschetst heb. Alle feedback is welkom.
Gewijzigd op 17/01/2019 17:50:15 door Johnny Cash
Heb je misschien nog een andere tip naast die link.?
Toevoeging op 18/01/2019 10:11:20:
Beste Adoptive Solutions,
Ik heb zowel de voorbeelden uit Example #11 DOM Interoperability en ook #10 geprobeerd.
Geen van beiden geeft het gewenste resultaat.
Ik kreeg steeds de melding dat SimpleXMLElement of loadXML een String verwacht maar dat ik of een array of een object geef. Hoe kan ik dit nu converteren? I"m lost in deze bewerking.
Gebaseerd op :
https://stackoverflow.com/questions/4194489/how-to-parse-soap-xml
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
$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>2019-01-05T15:19:45</postingDate>
<bankCurrency>EUR</bankCurrency>
<bankAmount>16.00</bankAmount>
<appliedCurrency>EUR</appliedCurrency>
<appliedAmount>1.00</appliedAmount>
<countryCode>NL</countryCode>
<bankInformation>Sean Wood</bankInformation>
<merchantReference>ESDEUR11039872</merchantReference>
</payment>
</PaymentNotification>
<PaymentNotification xmlns="http://apilistener.envoyservices.com">
<payment>
<uniqueReference>ESDEUR11040226</uniqueReference>
<epacsReference>74348dc0-cbf0-df11-b725-001ec9e61285</epacsReference>
<postingDate>2019-01-15T15:19:45</postingDate>
<bankCurrency>EUR</bankCurrency>
<bankAmount>441.00</bankAmount>
<appliedCurrency>EUR</appliedCurrency>
<appliedAmount>1.00</appliedAmount>
<countryCode>ES</countryCode>
<bankInformation>Paul Newman</bankInformation>
<merchantReference>ESDEUR11040226</merchantReference>
</payment>
</PaymentNotification>
</soap:Body>
</soap:Envelope>';
$response = strtr( $xml, ['</soap:' => '</', '<soap:' => '<'] );
$output = json_decode( json_encode( simplexml_load_string( $response ) ) );
//var_dump($output->Body->PaymentNotification->payment);
echo '<pre>' . print_r( $output, TRUE ) . '</pre>';
?>
$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>2019-01-05T15:19:45</postingDate>
<bankCurrency>EUR</bankCurrency>
<bankAmount>16.00</bankAmount>
<appliedCurrency>EUR</appliedCurrency>
<appliedAmount>1.00</appliedAmount>
<countryCode>NL</countryCode>
<bankInformation>Sean Wood</bankInformation>
<merchantReference>ESDEUR11039872</merchantReference>
</payment>
</PaymentNotification>
<PaymentNotification xmlns="http://apilistener.envoyservices.com">
<payment>
<uniqueReference>ESDEUR11040226</uniqueReference>
<epacsReference>74348dc0-cbf0-df11-b725-001ec9e61285</epacsReference>
<postingDate>2019-01-15T15:19:45</postingDate>
<bankCurrency>EUR</bankCurrency>
<bankAmount>441.00</bankAmount>
<appliedCurrency>EUR</appliedCurrency>
<appliedAmount>1.00</appliedAmount>
<countryCode>ES</countryCode>
<bankInformation>Paul Newman</bankInformation>
<merchantReference>ESDEUR11040226</merchantReference>
</payment>
</PaymentNotification>
</soap:Body>
</soap:Envelope>';
$response = strtr( $xml, ['</soap:' => '</', '<soap:' => '<'] );
$output = json_decode( json_encode( simplexml_load_string( $response ) ) );
//var_dump($output->Body->PaymentNotification->payment);
echo '<pre>' . print_r( $output, TRUE ) . '</pre>';
?>
En deze :
https://stackoverflow.com/questions/21777075/how-to-convert-soap-response-to-php-array
Met dezelfde xml, maar nu als bestand.
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
<?php
$response = file_get_contents( 'soap.xml' );
$xml = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $response );
$xml = simplexml_load_string($xml);
$json = json_encode($xml);
$responseArray = json_decode($json,true);
echo '<pre>' . print_r( $responseArray, TRUE ) . '</pre>';
?>
$response = file_get_contents( 'soap.xml' );
$xml = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $response );
$xml = simplexml_load_string($xml);
$json = json_encode($xml);
$responseArray = json_decode($json,true);
echo '<pre>' . print_r( $responseArray, TRUE ) . '</pre>';
?>
Thank voor het zoekwerk. Echter ben ik deze beiden ook al tegengekomen.
Het issue is en blijft bij mij dat wanneer ik de examples verwerk in mijn kleine stukje script dat ik nog steeds de foutmeldingen blijf behouden van "function expect a string, object is given" of "expect a string, array is given".
Ik moet dus op één of andere manier de response die ik terug krijg omzetten naar een werkbaar XML bestand om daar vervolgens iets me te kunnen doen m.b.v. PHP.
Als ik de print_r functie gebruik voor de variabele "$WebmethodResult" dan krijg ik dit te zien op m'n scherm:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$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>
<GetShipmentStatusResult>
<schema></schema>
<any>18237412Laadscan depotP78RayonAfgeleverdA47Rayon AfgeleverdA47Rayon</any>
</GetShipmentStatusResult>
</soap:Body>
</soap:Envelope>';
$response = strtr( $xml, ['</soap:' => '</', '<soap:' => '<'] );
$output = json_decode( json_encode( simplexml_load_string( $response ) ) );
//var_dump($output->Body->PaymentNotification->payment);
echo '<pre>' . print_r( $output, TRUE ) . '</pre>';
$output = $output->Body;
echo $output->GetShipmentStatusResult->any . '<br />';
?>
$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>
<GetShipmentStatusResult>
<schema></schema>
<any>18237412Laadscan depotP78RayonAfgeleverdA47Rayon AfgeleverdA47Rayon</any>
</GetShipmentStatusResult>
</soap:Body>
</soap:Envelope>';
$response = strtr( $xml, ['</soap:' => '</', '<soap:' => '<'] );
$output = json_decode( json_encode( simplexml_load_string( $response ) ) );
//var_dump($output->Body->PaymentNotification->payment);
echo '<pre>' . print_r( $output, TRUE ) . '</pre>';
$output = $output->Body;
echo $output->GetShipmentStatusResult->any . '<br />';
?>
xml kan je laten zien met deze regel
Hoe vul ik de $xml variabele?
In jouw voorbeeld gaat je er vanuit dat ik deze $xml gevuld heb.
Echter is alleen de variabele $WebmethodResult gevuld met de response. En deze response laat hetgeen zien ik gepost heb om 11:28
Moet ik dan zeggen: $xml = $WebmethodResult; ??
Want dat krijg ik wederom dezelfde melding "... expects parameter 1 to be string, object given"
Gewijzigd op 18/01/2019 13:01:16 door Johnny Cash
Weet je zeker dat je niet ergens de tweede parameter van json_decode() mist, deze zou true moeten zijn. Dit zorgt ervoor dat objecten worden omgezet naar associatieve arrays.
Ik heb in een ander voorbeeld inderdaad ook iets geprobeerd met json_decode() waarbij de tweede parameter true was, maar ook daarbij krijg ik een foutmelding dat er een string wordt verwacht en niet een object of array.
Afgelopen weekend heb ik hier nog eens goed naar gekeken, maar ik krijg elke keer het stdClass Object terug met gegevens. Hoe kan ik deze nou uitlezen?
json_decode($json, true);
En dan bevat $json de waarden uit $WebmethodResult als ik het goed begrijp?
Ja
Warning: json_decode() expects parameter 1 to be string, object given
$defarray = json_decode($WebmethodResult, true);
$WebmethodResult bestaat dus uit objecten welke ik niet kan decoden met json_decode($WebmethodResult, true);
Typecasting wilt soms wel eens een handje helpen.
Ik kwam ook dit tegen tijdens een zoektocht op internet:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
function object_to_array($data)
{
if (is_array($data) || is_object($data))
{
$result = array();
foreach ($data as $key => $value)
{
$result[$key] = object_to_array($value);
}
return $result;
}
return $data;
}
{
if (is_array($data) || is_object($data))
{
$result = array();
foreach ($data as $key => $value)
{
$result[$key] = object_to_array($value);
}
return $result;
}
return $data;
}
Gewijzigd op 21/01/2019 15:43:17 door - Ariën -
Warning: json_decode() expects parameter 1 to be string, array given in....
Er wordt dus een string verwacht en niet een array of object.
De gedefinieerde functie object_to_array had ik inderdaad ook al geprobeerd.
Gewijzigd op 21/01/2019 16:09:21 door Johnny Cash
In wezen doe je met $client->getShipmentStatus($parameters) een aanroep van __soapCall().
Hier staat (onder Return Values):
Quote:
On error, if the SoapClient object was constructed with the exceptions option set to FALSE, a SoapFault object will be returned.
Ik weet niet of dit de default is, maar ik denk wel dat je er verstandig aan doet om ofwel een try-catch mechanisme te gebruiken, ofwel te controleren of er fouten optreden middels is_soap_fault(), afhankelijk van wat je instelt via de exceptions optie. Ook lijkt het mij handig en verstandig zoveel mogelijk expliciet te configureren, zodat je ook zeker weet dat de code ook op een voorgeschreven manier werkt want defaults zijn immers niet altijd de default.
Vervolgens geeft getShipmentStatus() iets terug. De documentatie van soapCall zegt hierover:
Quote:
SOAP functions may return one, or multiple values. If only one value is returned by the SOAP function, the return value of __soapCall will be a simple value (e.g. an integer, a string, etc). If multiple values are returned, __soapCall will return an associative array of named output parameters.
Dus ik zou in eerste instantie een array verwachten. Maar jij zegt dat het een object is of zelfs XML. Wat is het nu? :p Heeft deze webservice ook een documentatie, staat daar in beschreven wat je terug kunt verwachten?
Maar het lijkt er dus op dat alles min of meer goed verloopt (foutafhandeling en configuratie daar gelaten) en je dus enkel een waarde ergens uit moet peuteren (een object? een lap XML? iets anders?), maar dat staat verder los van dit hele SOAP gebeuren lijkt mij.
Gewijzigd op 21/01/2019 17:19:00 door Thomas van den Heuvel
De aanroep die ik doe geeft inderdaad een response terug.
Via onderstaande code toon ik op m'n scherm wat er terugkomt, namelijk:
Op het scherm ziet dat er uit zoals:
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
stdClass Object
(
[GetShipmentStatusResult] => stdClass Object
(
[schema] =>
[any] => 18237421 2019-01-15AfgeleverdA47Rayon
)
)
(
[GetShipmentStatusResult] => stdClass Object
(
[schema] =>
[any] => 18237421 2019-01-15AfgeleverdA47Rayon
)
)
Als ik naar de Broncode kijk van deze response die ik op het scherm krijg ziet dat er als volgt uit:
Code (php)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
<pre>stdClass Object
(
[GetShipmentStatusResult] => stdClass Object
(
[schema] => <xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="NewDataSet"><xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"> .....etc. etc..... </xs:schema>
[any] => <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"><NewDataSet xmlns=""><Shipment diffgr:id="Shipment1" msdata:rowOrder="0"><ShipmentID>18237421</ShipmentID><Country>NL</Country><MovementType>Tijdvenster</MovementType><Code>00</Code>.....etc. etc.....</Shipment></NewDataSet></diffgr:diffgram>
)
)
</pre>
(
[GetShipmentStatusResult] => stdClass Object
(
[schema] => <xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="NewDataSet"><xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"> .....etc. etc..... </xs:schema>
[any] => <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"><NewDataSet xmlns=""><Shipment diffgr:id="Shipment1" msdata:rowOrder="0"><ShipmentID>18237421</ShipmentID><Country>NL</Country><MovementType>Tijdvenster</MovementType><Code>00</Code>.....etc. etc.....</Shipment></NewDataSet></diffgr:diffgram>
)
)
</pre>
Voor de toepassing die ik wil maken wil ik graag het element "MovementType" gaan uitlezen, omdat daarin een status wordt gegeven. Daarom moet ik dus op een correcte manier de variabele $WebmethodResult uitlezen in PHP.
Anyhow, het volgende lijkt ongeveer te doen wat jij wilt, bij wijze van proof of concept. Iets soortgelijks zou voor bovenstaande SOAP-response ook moeten werken:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php
// zorg dat output goed geencodeerd is
header('Content-Type: text/html; charset=UTF-8');
// voor het melden+weergeven van foutmeldingen voor debugging
error_reporting(E_ALL);
ini_set('display_startup_errors', true);
ini_set('display_errors', 'stdout');
// hulpfunctie voor het (veilig) dumpen van data
function dump($in) {
if (is_array($in)) {
$in = print_r($in, true);
}
echo '<pre>'.escape($in).'</pre>';
}
// hulpfunctie voor het escapen van output
function escape($in) {
return htmlspecialchars($in, ENT_QUOTES, 'UTF-8');
}
// Test case, bouw een soortgelijk genest object met hierin een XML-string
$wrapper = new StdClass();
$subObj = new StdClass();
$subObj->xml = '<root><data>hoi</data></root>';
$wrapper->sub = $subObj;
// zet objecten om naar (geneste) arrays
$response = json_decode(json_encode($wrapper), true);
// inspecteer output
dump($response);
// selecteer de XML-string
$xml = $response['sub']['xml'];
// creeer hiermee een SimpleXMLElement object
$data = simplexml_load_string($xml);
// converteer dit wederom naar (geneste) arrays
$dataArray = json_decode(json_encode($data), true);
// And Bob's your uncle
dump($dataArray);
?>[end]
// zorg dat output goed geencodeerd is
header('Content-Type: text/html; charset=UTF-8');
// voor het melden+weergeven van foutmeldingen voor debugging
error_reporting(E_ALL);
ini_set('display_startup_errors', true);
ini_set('display_errors', 'stdout');
// hulpfunctie voor het (veilig) dumpen van data
function dump($in) {
if (is_array($in)) {
$in = print_r($in, true);
}
echo '<pre>'.escape($in).'</pre>';
}
// hulpfunctie voor het escapen van output
function escape($in) {
return htmlspecialchars($in, ENT_QUOTES, 'UTF-8');
}
// Test case, bouw een soortgelijk genest object met hierin een XML-string
$wrapper = new StdClass();
$subObj = new StdClass();
$subObj->xml = '<root><data>hoi</data></root>';
$wrapper->sub = $subObj;
// zet objecten om naar (geneste) arrays
$response = json_decode(json_encode($wrapper), true);
// inspecteer output
dump($response);
// selecteer de XML-string
$xml = $response['sub']['xml'];
// creeer hiermee een SimpleXMLElement object
$data = simplexml_load_string($xml);
// converteer dit wederom naar (geneste) arrays
$dataArray = json_decode(json_encode($data), true);
// And Bob's your uncle
dump($dataArray);
?>[end]
EDIT: als dat XML-ding onhandig in elkaar zit (met attributen enzo) dan zul je deze misschien wat verder uit elkaar moeten trekken m.b.v. SimpleXML, maar als je eenmaal het XML-deel bij zijn kladden hebt zou dat niet ingewikkeld moeten zijn.
Gewijzigd op 21/01/2019 22:46:24 door Thomas van den Heuvel
Echter krijg ik wederom de volgende foutmelding:
simplexml_load_string() expects parameter 1 to be string, array given in
Op basis van jouw concept krijg ik het volgende op mijn scherm (output):
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Array
(
[sub] => Array
(
[xml] => Array
(
[GetShipmentStatusResult] => Array
(
[schema] => <xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="NewDataSet"> .... etc. etc. .... </xs:schema>
[any] => <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"><NewDataSet xmlns=""><Shipment diffgr:id="Shipment1" msdata:rowOrder="0"><ShipmentID>18237421</ShipmentID></Shipment></NewDataSet></diffgr:diffgram>
)
)
)
)
(
[sub] => Array
(
[xml] => Array
(
[GetShipmentStatusResult] => Array
(
[schema] => <xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="NewDataSet"> .... etc. etc. .... </xs:schema>
[any] => <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"><NewDataSet xmlns=""><Shipment diffgr:id="Shipment1" msdata:rowOrder="0"><ShipmentID>18237421</ShipmentID></Shipment></NewDataSet></diffgr:diffgram>
)
)
)
)
Echter krijg ik nog steeds geen echo van het element "ShipmentID" op m'n scherm op één of andere manier.
Dat moet je nog steeds extraheren uit je schema en any velden.