I've tried many pre built parsers including the xml2array parser with no luck. I've also tried my own without any success either. I've tried simplexmlelement/xpath and a bunch of different ways with the same result so I'm at a place where I'm banging my head on my keyboard.
Basically I'm trying to parse code the API call is spitting back to me. I'm making a soap call and getting the xml content via curl (https).
Here is an example of the xml I'm getting back:
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Powered-By: Servlet 2.5; JBoss-5.0/JBossWeb-2.1
Content-Type: text/xml;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 21 Apr 2012 19:49:30 GMT
<env:Envelope xmlns:env='http://schemas.xmlsoap.org/soap/envelope/'>
<env:Header></env:Header>
<env:Body>
<ns:processRequestResponse xmlns:ns='hidden.address'>
<result>
<?xml version="1.0" encoding="UTF-8"?>
<CRMMessage language="en_US" currency="USD" isTrustedSAT="false" hostversion="hiddenINT">
<RequestCode>HiddenMethoForCall</RequestCode>
<ResponseCode>A</ResponseCode>
<ResultSet>
<ResultSetMetaData>
<RSColumn name="FirstName" type="string" nullable="true"></RSColumn>
<RSColumn name="LastName" type="string" nullable="true"></RSColumn>
<RSColumn name="EmailAddress" type="string" nullable="true"></RSColumn>
</ResultSetMetaData>
<Rows>
<Row id="hiddenINT">
<Col>John</Col>
<Col>Doe</Col>
<Col>john#doe.com</Col>
</Row>
</Rows>
</ResultSet>
</CRMMessage>
</result>
</ns:processRequestResponse>
</env:Body>
</env:Envelope>
I tried using xml2array, but that explicitly states that it doesn't support parsing same name keys in the same level.
I'm not sure if it will matter, as I'm open to any parsers suggested, but here is one that I put together (this is inside the class that handles the request after the request is made):
$retVal = curl_exec($soap_do);
if($retVal === false) {
$err = 'Curl error: ' . curl_error($soap_do);
throw new requestManager($err);
} else {
$string = strstr($retVal, '<');
// $aResp = simplexml_load_string($string);
if(!function_exists('xml_parser_create')) {
//print "'xml_parser_create()' function not found!";
return array();
}
curl_close($soap_do);
//custom parse
$aResp = array();
$i=0;
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
// xml_parse_into_struct($parser, $string, $values, $tags);
xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
xml_parse_into_struct($parser, trim($string), $values);
xml_parser_free($parser);
// var_dump($values);
foreach($values as $val) {
foreach($val as $key=>$value) {
if($key=='value'){
if(is_array($value)) {
foreach($value as $k=>$v) {
echo $v;
$value[$i]=$value;
}
}else {
$aResp[$i]=$value;
echo '<h1>'.$key.'</h1>';
echo '<h2>'.$value.'</h2><br>';
}
$i++;
}
}
}
}
Any help would be appreciated as I'm hitting a wall here. I've checked every resource I can think of with no luck. Thank you!
Related
I have been spending hours trying to parse a SOAP response that I have no control over. I have tried numerous methods I found on SO with no luck.
Here is the response body I get from edge browser:
<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<ns1:gXMLQueryResponse xmlns:ns1="urn:com-photomask-feconnect-IFeConnect" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<return xsi:type="xsd:string"><?xml version = '1.0' encoding = 'UTF-8'?>
<ROWSET>
<ROW num="1">
<CUSTOMER_NAME>HITACHI</CUSTOMER_NAME>
</ROW>
</ROWSET>
</return>
</ns1:gXMLQueryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I'm trying to get the CUSTOMER_NAME value.
Here is the code I am using:
$client = new SoapClient($urla, array('trace' => 1));
try {
$result = $client->__soapCall("gXMLQuery", $params);
$response = ($client->__getLastResponse());
$xml = simplexml_load_string($response);
$rows = $xml->children('SOAP-ENV', true)->Body->children('ns1', true)->gXMLQueryResponse->return->ROWSET->ROW;
foreach ($rows as $row)
{
$customer = $row->CUSTOMER_NAME;
echo $customer;
}
} catch (SoapFault $e) {
}
return is a string, you need to parse it first before you can access it using SimpleXML.
First you need to decode the string using html_entity_decode, after that you can load the decoded string with simplexml_load_string:
$return = $xml->children('SOAP-ENV', true)->Body->children('ns1', true)->gXMLQueryResponse->return;
$decodedReturn = html_entity_decode($return, ENT_QUOTES | ENT_XML1, 'UTF-8');
$rowset = simplexml_load_string($decodedReturn);
echo $rowset->ROW->CUSTOMER_NAME;
I have a SOAP response as follows:
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope" xmlns="">
<faultcode>stuff</faultcode>
<faultstring>stuff</faultstring>
<detail>
<ns2:Exception xmlns:ns2="http://blah.com/">
<message>stuff</message>
</ns2:Exception>
</detail>
</S:Fault>
</S:Body>
</S:Envelope>
I need to extract faultcode, faultstring, and message.
I have tried SimpleXML_load_string, SimpleXMLElement, DOMDocument, registerXPathNamespace and json_decode but can't seem to get the exact procedure correct because I get errors instead of results. Thanks in advance.
Latest attempt:
$xmle1 = SimpleXML_load_string(curl_exec($ch));
if (curl_errno($ch)) {
print "curl error: [" . curl_error($ch) . "]";
} else {
$xmle1->registerXPathNamespace('thing1', 'http://blah.com/');
foreach ($xml->xpath('//thing1:message') as $item) {
echo (string) $item;
}
<?php
$string = <<<XML
<?xml version="1.0" ?>
<S:Envelope
xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<S:Fault
xmlns:ns4="http://www.w3.org/2003/05/soap-envelope"
xmlns="">
<faultcode>stuff</faultcode>
<faultstring>stuff</faultstring>
<detail>
<ns2:Exception
xmlns:ns2="http://blah.com/">
<message>stuff</message>
</ns2:Exception>
</detail>
</S:Fault>
</S:Body>
</S:Envelope>
XML;
$_DomObject = new DOMDocument;
$_DomObject->loadXML($string);
if (!$_DomObject) {
echo 'Error while parsing the document';
exit;
}
$s = simplexml_import_dom($_DomObject);
foreach(['faultcode','faultstring','message'] as $tag){
echo $tag.' => '.$_DomObject->getElementsByTagName($tag)[0]->textContent.'<br/>';
}
?>
outputs
faultcode => stuff
faultstring => stuff
message => stuff
you might want to write a class to parse the XML string and build an nice Fault object with methods for easier access after you parse it.
I am trying integrate a payment gateway in a simple PHP site (my own site) and the gateway accepts data only in SOAP format. I have absolutely no idea what the SOAP is, but thanks to Google I now know how it looks like (at least).
Basically, I need to send a bunch of customer data and payment data to the gateway to act according to the response receive. Here are the sample request code and sample response code. They only provided the URLto post to and that is http://69.94.141.22/SaveTransactions.asmx.
Request
POST /SaveTransactions.asmx HTTP/1.1
Host: 69.94.141.22
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<SendTransactionsAction xmlns="http://tempuri.org/">
<strUserName>string</strUserName>
<strPassword>string</strPassword>
<strSecureKey>string</strSecureKey>
<strFirstName>string</strFirstName>
<strLastName>string</strLastName>
<strPhoneNumber>string</strPhoneNumber>
<strStreetNumber>string</strStreetNumber>
<strUnitNumber>string</strUnitNumber>
<strStreetName>string</strStreetName>
<strCity>string</strCity>
<strState>string</strState>
<strZipCode>string</strZipCode>
<strEmailAddress>string</strEmailAddress>
<strBankName>string</strBankName>
<strRoutingNo>string</strRoutingNo>
<strAccountNumber>string</strAccountNumber>
<strCheckNo>string</strCheckNo>
<strAmount>string</strAmount>
<strNotes>string</strNotes>
</SendTransactionsAction>
</soap12:Body>
</soap12:Envelope>
Response
HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<SendTransactionsActionResponse xmlns="http://tempuri.org/">
<SendTransactionsActionResult>string</SendTransactionsActionResult>
</SendTransactionsActionResponse>
</soap12:Body>
</soap12:Envelope>
How do I post these to the URL provided using PHP and how do I get that response SendTransactionsActionResult from the returned response?
I am not asking you to do it for me, but a simple get started like codes will help a lot.
Thanks in advance
You can archive this by using Curl, php soapClient or NuSOAP
Below i have shown you how to use NuSOAP library
Also the WSDL is location in http://69.94.141.22/SaveTransactions.asmx?WSDL
$client = new nusoap_client("http://69.94.141.22/SaveTransactions.asmx?WSDL", true); // this should be the wsdl location
$error = $client->getError();
if ($error) {
echo "<h2>Constructor error</h2><pre>" . $error . "</pre>";
}
// Use basic authentication method
$client->setCredentials("username", "password", "basic"); //set here the credentials if need for the wsdl
$client->setHeaders('<SendTransactionsAction xmlns="http://tempuri.org/">
<strUserName>string</strUserName>
<strPassword>string</strPassword>
<strSecureKey>string</strSecureKey>
<strFirstName>string</strFirstName>
<strLastName>string</strLastName>
<strPhoneNumber>string</strPhoneNumber>
<strStreetNumber>string</strStreetNumber>
<strUnitNumber>string</strUnitNumber>
<strStreetName>string</strStreetName>
<strCity>string</strCity>
<strState>string</strState>
<strZipCode>string</strZipCode>
<strEmailAddress>string</strEmailAddress>
<strBankName>string</strBankName>
<strRoutingNo>string</strRoutingNo>
<strAccountNumber>string</strAccountNumber>
<strCheckNo>string</strCheckNo>
<strAmount>string</strAmount>
<strNotes>string</strNotes>
</SendTransactionsAction>
');
$result = "";
if ($client) {
$result = $client->call("SendTransactionsAction"); // soap action
}
if ($client->fault) {
echo "<h2>Fault</h2><pre>";
print_r($result);
echo "</pre>";
}
else {
$error = $client->getError();
if ($error) {
echo "<h2>Error</h2><pre>" . $error . "</pre>";
}
else {
echo "<h2>zip code</h2><pre>";
print_r($result);
echo "</pre>";
}
}
echo "<h2>Request</h2>";
echo "<pre>" . htmlspecialchars($client->request, ENT_QUOTES) . "</pre>";
echo "<h2>Response</h2>";
echo "<pre>" . htmlspecialchars($client->response, ENT_QUOTES) . "</pre>";
Using the WSDL from http://69.94.141.22/SaveTransactions.asmx?WSDL, you could generate the corresponding package from wsdltophp.com in order to be sure on how to structure your request in PHP as every element will be a PHP object with setters/getters. It uses the native PHP SoapClient class so you'll understand easily and quickly who to send these requests if you're familiar with PHP
in my site in PHP I want to read an xml file like this:
<?xml version="1.0" standalone="yes"?>
<RETURNDATA lang="it-IT" type="COR" xsi:noNamespaceSchemaLocation="http://xmlv5test.travco.co.uk/trlink/schema/CountryRequestV6Rcv.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MESSAGE>All Countries details and relevant city details</MESSAGE>
<DATA COUNTRY_CODE="ABW" CURRENCY_CODE="EUR">
<COUNTRY_NAME>Aruba</COUNTRY_NAME>
<CURRENCY_NAME>euro</CURRENCY_NAME>
<COUNTRY_CITIES>
<CITY_DATA CITY_CODE="AUA">
<CITY_NAME>Aruba</CITY_NAME>
</CITY_DATA>
</COUNTRY_CITIES>
</DATA>
<DATA COUNTRY_CODE="ALB" CURRENCY_CODE="EUR">
<COUNTRY_NAME>Albania</COUNTRY_NAME>
<CURRENCY_NAME>euro</CURRENCY_NAME>
<COUNTRY_CITIES>
<CITY_DATA CITY_CODE="TIA">
<CITY_NAME>Tirana</CITY_NAME>
</CITY_DATA>
</COUNTRY_CITIES>
</DATA>
<DATA COUNTRY_CODE="ARE" CURRENCY_CODE="EUR">
<COUNTRY_NAME>Emirati Arabi Uniti</COUNTRY_NAME>
<CURRENCY_NAME>euro</CURRENCY_NAME>
<COUNTRY_CITIES>
<CITY_DATA CITY_CODE="DXB">
<CITY_NAME>Dubai</CITY_NAME>
</CITY_DATA>
<CITY_DATA CITY_CODE="AAI">
<CITY_NAME>Al Ain</CITY_NAME>
</CITY_DATA>
<CITY_DATA CITY_CODE="FJR">
<CITY_NAME>Fujaira</CITY_NAME>
</CITY_DATA>
<CITY_DATA CITY_CODE="SSH">
<CITY_NAME>Sharja</CITY_NAME>
</CITY_DATA>
<CITY_DATA CITY_CODE="RKT">
<CITY_NAME>Ras al-Khaimah</CITY_NAME>
</CITY_DATA>
<CITY_DATA CITY_CODE="AUH">
<CITY_NAME>Abu Dhabi</CITY_NAME>
</CITY_DATA>
</COUNTRY_CITIES>
</DATA>
</RETURNDATA>
I want to enter in each node name DATA and take:
COUNTRY_CODE
CURRENCY_CODE
COUNTRY_NAME
CURRENCY_NAME
And all ountry cities code and name into an array associative.
I have tried with SimpleXML, but the XML is dynamic and I wanto to optimize my cycle because I can have a very big and large XML (this is only a little part of It).
$xml_str = file_get_contents('xml/country.xml');
$xml = new SimpleXMLElement($xml_str);
echo $xml->getName(), PHP_EOL;
foreach($xml as $name => $part) {
echo "$name: $part", PHP_EOL;
}
I want to create a very otpimize cycle to take my value
Use the following style to acces only the <DATA> child elements in the root element:
foreach ($xml->DATA as $name => $part) {
Also please check the SimpleXML Introduction it should have some good examples for you showing different ways how to do the basic stuff with SimpleXML. The documentation is pretty good.
you could use the XML parser directly, this would more than likely be the most efficient way
set_time_limit(0);
define('__BUFFER_SIZE__', 131072);
define('__XML_FILE__', 'pf_1360591.xml');
function elementStart($p, $n, $a) {
//handle opening of elements
}
function elementEnd($p, $n) {
//handle closing of elements
}
function elementData($p, $d) {
//handle cdata in elements
}
$xml = xml_parser_create();
xml_parser_set_option($xml, XML_OPTION_TARGET_ENCODING, 'UTF-8');
xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
xml_set_element_handler($xml, 'elementStart', 'elementEnd');
xml_set_character_data_handler($xml, 'elementData');
$f = fopen(__XML_FILE__, 'r');
if($f) {
while(!feof($f)) {
$content = fread($f, __BUFFER_SIZE__);
xml_parse($xml, $content, feof($f));
unset($content);
}
fclose($f);
}
I'm using cURL to POST a SOAP request. The response is as follows:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:To>http://www.w3.org/2005/08/addressing/anonymous</wsa:To>
<wsa:Action>http://www.csapi.org/schema/parlayx/common/v3_1/TerminalLocationPort/getLocationForGroupResponse</wsa:Action>
</env:Header>
<env:Body>
<ns2:getLocationForGroupResponse xmlns:ns2="http://www.csapi.org/schema/parlayx/terminal_location/v3_1/local">
<ns2:result>
<address>234983</address>
<reportStatus>Retrieved</reportStatus>
<currentLocation>
<latitude>12.5665</latitude>
<longitude>43.7708</longitude>
<timestamp>2012-01-03T17:06:16.805+01:30</timestamp>
</currentLocation>
</ns2:result>
<ns2:result>
<address>423903</address>
<reportStatus>Retrieved</reportStatus>
<currentLocation>
<latitude>12.2165</latitude>
<longitude>43.6518</longitude>
<timestamp>2012-01-03T17:06:16.824+01:30</timestamp>
</currentLocation>
</ns2:result>
</ns2:getLocationForGroupResponse>
</env:Body>
</env:Envelope>
I use this to decode:
$err = curl_error($soap_do);
$result = curl_exec($soap_do);
$xml = simplexml_load_string($result);
$ns = $xml->getNamespaces(true);
$soap = $xml->children($ns['env']);
$getaddressresponse = $soap->body->children($ns['ns2']);
foreach ($getaddressresponse->children() as $item) {
echo (string) $item->address . '<br />';
}
I'm having trouble decoding this with SimpleXML. This link seems most relevant to my situation but I'm unable to apply it to my case as the simpleXML element just
Warning: SimpleXMLElement::children() [simplexmlelement.children]: Node no longer exists in C:\.php on line 33 /*(line with the for each statement)*/
Any suggestions?
UPDATE:
If the server responds with the following error, how would I detect it..?
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:To>http://www.w3.org/2005/08/addressing/anonymous</wsa:To>
<wsa:Action>http://www.w3.org/2005/08/addressing/fault</wsa:Action>
</env:Header>
<env:Body>
<env:Fault>
<faultcode>env:Server</faultcode>
<faultstring>Service Exception</faultstring>
<detail>
<ns1:ServiceException xmlns:ns1="http://www.csapi.org/schema/parlayx/common/v3_1" xmlns:ns2="http://www.csapi.org/schema/parlayx/terminal_location/v3_1/local">
<messageId>SVC004</messageId>
<text>Trip not Found for this MSISDN</text>
</ns1:ServiceException>
</detail>
</env:Fault>
</env:Body>
</env:Envelope>
Variable and property names are case sensitive and while I was testing it, it turned out there's other stuff as well. The following works:
$soap = $xml->children($ns['env']);
$getaddressresponse = $soap->Body->children($ns['ns2']);
foreach ($getaddressresponse->getLocationForGroupResponse->children($ns['ns2']) as $item)
{
$item = $item->children();
echo $item->address . '<br />';
}
To answer the updated question:
$fault = $soap->Body->children($ns['env']);
if (isset($fault->Fault))
{
// Handle error
}