parse an XML with SimpleXML which has multiple namespaces [duplicate] - php

This question already has answers here:
Reference - How do I handle Namespaces (Tags and Attributes with a Colon in their Name) in SimpleXML?
(2 answers)
Closed 3 years ago.
I have this ugly XML which has alot of namespaces on it, when I try to load it with simpleXML if i indicate the first namespace I'd get an xml object ,but following tags with other namespaces would not make it to the object.
How can I parse this XML ?
<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header>
<eb:MessageHeader xmlns:eb="http://www.ebxml.org/namespaces/messageHeader" eb:version="1.0" soap-env:mustUnderstand="1">
<eb:From>
<eb:PartyId eb:type="URI">wscompany.com</eb:PartyId>
</eb:From>
<eb:To>
<eb:PartyId eb:type="URI">mysite.com</eb:PartyId>
</eb:To>
<eb:CPAId>something</eb:CPAId>
<eb:ConversationId>moredata.com</eb:ConversationId>
<eb:Service eb:type="compXML">theservice</eb:Service>
<eb:Action>theaction</eb:Action>
<eb:MessageData>
<eb:MessageId>a certain messageid</eb:MessageId>
<eb:Timestamp>2009-04-11T18:43:58</eb:Timestamp>
<eb:RefToMessageId>mid:areference</eb:RefToMessageId>
</eb:MessageData>
</eb:MessageHeader>
<wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext">
<wsse:BinarySecurityToken valueType="String" EncodingType="wsse:Base64Binary">an impresive binary security toekn</wsse:BinarySecurityToken>
</wsse:Security>
</soap-env:Header>
<soap-env:Body>
<SessionCreateRS xmlns="http://www.opentravel.org/OTA/2002/11" version="1" status="Approved">
<ConversationId>the goodbye token</ConversationId>
</SessionCreateRS>
</soap-env:Body>
</soap-env:Envelope>
im trying to parse it with the following code
<?php
$xml = simplexml_load_string($res,NULL,NULL,"http://schemas.xmlsoap.org/soap/envelope/");
?>
but the $xml object would only contain the following
SimpleXMLElement Object
(
[Header] => SimpleXMLElement Object
(
)
[Body] => SimpleXMLElement Object
(
)
)

I think you need to register the namespacing and access with XPath. Something like the following should get you going (I haven't the facility to test this).
$xml = simplexml_load_string($res, NULL, NULL, "http://schemas.xmlsoap.org/soap/envelope/");
$xml->registerXPathNamespace('soap-env', 'http://schemas.xmlsoap.org/soap/envelope/');
$xml->registerXPathNamespace('eb', 'http://www.ebxml.org/namespaces/messageHeader');
$xml->registerXPathNamespace('wsse', 'http://schemas.xmlsoap.org/ws/2002/12/secext');
Then you can do something like:
foreach($xml->xpath('//eb:MessageHeader') as $header)
{
var_export($header->xpath('//eb:CPAId')); // Should output 'something'.
}
You may not need to register the namespacing, thinking about it, as they are alredy present in the XML. Not sure on this though, would need to test.
Hope this helps.

1) Do not use print_r and friends to see what is "in" a SimpleXML object. See https://github.com/IMSoP/simplexml_debug for explanation and alternatives.
2) Namespace support in SimpleXML is provided by the ->children() and ->attributes() methods.
For example you could get the PartyId of the From node like this:
$from_party = (string)$xml->children('soap-env', true)->Header->children('eb', true)->MessageHeader->From->PartyId;

For anyone else that comes across this I scratched my head trying to return the correct data and although the top answer was extremely close it still took me a while to find the answer. Eventually used this page to help: https://www.w3schools.com/php/func_simplexml_registerxpathnamespace.asp
I believe the for loop can directly access what you need. i.e.
foreach($xml->xpath('//eb:CPAId') as $header)
{
echo $header; // Should output 'something'.
}

That's a soap-envelope. You might want to use a soap-client to abstract all the xml-parsing away. PHP comes with a rather good soap-client included as default.

Try this
$soap_url = 'http://path/wsdl/somefile.wsdl';
$soap_client = new SoapClient($soap_url);
var_dump($soap_client->__getFunctions());
For more detail read here

Related

PHP - Generating correct SOAP data for Salesforce API

Can anyone help point me in the correct direction to generate valid XML to use with the Salesforce API. I normally avoid SOAP like the plague but have no choice here and it seems like I'm at the bottom of a vertical learning curve.
I've got a full WSDL which as far as I'm aware should cover every part of the request, including all the appropriate namespaces and complex types, but I just can't generate a request that matches their example.
A subset of the example request looks like the following -
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://soap.sforce.com/schemas/class/WebsiteAPI" xmlns:web1="http://soap.sforce.com/schemas/class/WebsiteAPIUtils">
<soapenv:Header>
<web:SessionHeader>
<web:sessionId> SESSION_ID_FROM_LOGIN_METHOD </web:sessionId>
</web:SessionHeader>
</soapenv:Header>
<soapenv:Body>
<web:upsertLeadOrUpdateAccount>
<web:leadFromWebsite>
<!--Optional:-->
<web1:salesforceId></web1:salesforceId>
...
By calling the relevant method I can get an <ns1:upsertLeadOrUpdateAccount> request (not the same namespace id but that doesn't really matter), with an empty <leadFromWebsite> element. However, I just can't work out how to get the <salesforceId> sub element in there with the correct namespace. Everything I do either seems to do nothing, or adds an "xsi-type" attribute to the parent rather than using the namespace prefix.
The closest I've got is the following, which adds the namespace to the root element, but seems to create a weird looking xsi-type="ns1:web1" attribute, rather than just prefixing all the elements with web1.
I do have complexTypes for everything in the WSDL so I'm sure there should be a way for the SOAP library to handle all the heavy lifting for me. I really don't want to resort to just generating the whole lot by hand.
$data = new stdclass();
$data->salesforceId = '123';
$args = [
'leadFromWebsite' => new SoapVar($data, SOAP_ENC_OBJECT, 'web1', 'http://soap.sforce.com/schemas/class/WebsiteAPIUtils')
];
Output -
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://soap.sforce.com/schemas/class/WebsiteAPIUtils"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ns2="http://soap.sforce.com/schemas/class/WebsiteAPI">
<SOAP-ENV:Header>
<ns2:SessionHeader>
<ns2:sessionId>123</ns2:sessionId>
</ns2:SessionHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns2:upsertLeadOrUpdateAccount>
<ns2:leadFromWebsite xsi:type="ns1:web1">
<salesforceId>123</salesforceId>
</ns2:leadFromWebsite>
</ns2:upsertLeadOrUpdateAccount>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I haven't tested this fully with the salesforce API yet, but I think I've managed to generate XML that looks correct - at least a lot closer than I was.
Seems my main issue was confusing type and node namespaces. I wanted to specify the namespace for the node, not the data type.
The following code generates xml that is much closer to the example requests -
$data = [];
$data[] = new SoapVar('123', XSD_STRING, null, null, 'salesforceId', 'http://soap.sforce.com/schemas/class/WebsiteAPIUtils');
$args = [
'leadFromWebsite' => new SoapVar($data, SOAP_ENC_OBJECT, null, null, 'leadFromWebsite', 'http://soap.sforce.com/schemas/class/WebsiteAPI')
];
$s->upsertLeadOrUpdateAccount($args);
XML -
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://soap.sforce.com/schemas/class/WebsiteAPIUtils"
xmlns:ns2="http://soap.sforce.com/schemas/class/WebsiteAPI">
<SOAP-ENV:Header>
<ns2:SessionHeader>
<ns2:sessionId>123</ns2:sessionId>
</ns2:SessionHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns2:upsertLeadOrUpdateAccount>
<ns2:leadFromWebsite>
<ns1:salesforceId>123</ns1:salesforceId>
</ns2:leadFromWebsite>
</ns2:upsertLeadOrUpdateAccount>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

SimpleXml Failed to Generate Object

I am using Soap for web service call and PHP for entire operation. When I send request I get the response as 341025COMPLETE. But when I select it for Source it prints the XML behind it.
We have passed it to simplexml_load_string function but it failed to generate the object for the same.
What will be wrong with simplexml_load_string? When that same string is hard coded it works when it passed through function parameter it failed.
XML that i am getting is :
<applicantscreening xmlns="https://www.example.com/xml/services/PSI"><response><reportid>00000</reportid><backgroundreport></backgroundreport><status>TEST</status></response></applicantscreening>
I think there should be something wrong in xml .
Can you please provide xml string ?
Look at the simple example :
$string = <<<XML
<?xml version='1.0'?>
<document>
<title>Forty What?</title>
<from>Joe</from>
<to>Jane</to>
<body>
I know that's the answer -- but what's the question?
</body>
</document>
XML;
$xml = simplexml_load_string($string);
print_r($xml);

PHP Parse Soap Response Issue - SimpleXMLElement

I'm having problems using PHP SimpleXMLElement and simpleSMLToArray() function to parse a SOAP Response. I'm getting the SOAP response from my SOAP Server just fine. I'm writing both the SOAP Client and Server in this case. I'm using NuSoap for the server. To me, the soap response looks perfect, but the PHP5 Soap Client doesn't seem to parse it. So, as in the past, I'm using SimpleXMLElement and the function simpleXMLToArray() from PHP.NET (http://php.net/manual/en/book.simplexml.php), but can't seem to get an array.
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="urn:eCaseWSDL">
<SOAP-ENV:Body>
<ns1:registerDocumentByPatientResponse xmlns:ns1="urn:eCaseWSDL">
<returnArray xsi:type="tns:ReturnResult">
<id xsi:type="xsd:int">138</id>
<method xsi:type="xsd:string">registerDocumentByPatient</method>
<json xsi:type="xsd:string">0</json>
<message xsi:type="xsd:string">success</message>
<error xsi:type="xsd:string">0</error>
</returnArray>
</ns1:registerDocumentByPatientResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
PHP Code from my class ($this references apply to my library code).
// Define SimpleXMLElement
$xml_element = new SimpleXMLElement($response_string); // SOAP XML
$name_spaces = $xml_element->getNamespaces(true);
var_dump($name_spaces);
$soap = $xml_element->children('ns1');
var_dump($soap);
$soap_array = $this->simpleXMLToArray($soap);
var_dump($soap_array);
return $soap_array;
I can see the namespaces; ns1, etc. But, the array is not returned. SimpleXMLElement looks like it's returning an object, but empty.
<pre>array(3) {
["SOAP-ENV"]=>
string(41) "http://schemas.xmlsoap.org/soap/envelope/"
["ns1"]=>
string(13) "urn:eCaseWSDL"
["xsi"]=>
string(41) "http://www.w3.org/2001/XMLSchema-instance"
}
object(SimpleXMLElement)#23 (0) {
}
bool(false)
Anyone have any ideas as to what I'm doing wrong. I must not have drunk enough coffee this morning. I'm tempted to just parse it with regular expressions.
SimpleXML creates a tree object, so you have to follow that tree to get to the nodes you want.
Also, you have to use the actual namespace URI when accessing it, e.g.: urn:eCaseWSDL instead of ns1:
Try this:
$soap = $xml_element->children($name_spaces['SOAP-ENV'])
->Body
->children($name_spaces['ns1'])
->registerDocumentByPatientResponse
->children();
var_dump((string)$soap->returnArray->id); // 138

Issues parsing xml array from curl call

I am having serious issues parsing this xml array using curl. I only need the click_id printed on each new row. Does someone have an example of how I can pull this. I am using curl and get the response below in the variable $result. I am using php.
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfClick xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://cakemarketing.com/api/1/">
<Click>
<click_id>7458165</click_id>
<request_session_id>7901644</request_session_id>
<click_date>2011-09-08T13:53:37.143</click_date>
<offer_id>10346</offer_id>
<advertiser_id>1050</advertiser_id>
<campaign_id>6527</campaign_id>
</Click>
<Click>
<click_id>7459318</click_id>
<request_session_id>7903011</request_session_id>
<click_date>2011-09-08T14:41:37.953</click_date>
<offer_id>10346</offer_id>
<advertiser_id>1050</advertiser_id>
<campaign_id>6527</campaign_id>
</Click>
The best way is to use XPath with the help of this class http://php.net/manual/en/class.domxpath.php
A more simple method is to use regex (but I discourage this approach)

Accessing XML content via PHP using SimpleXML

I am working with the XML file string below and I've tried a number of methods to try and get access to certain parts of the XML contents. Please see the code after the XML file below for my attempt:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<Address_ListResponse xmlns="http://example.example.com/">
<Address_ListResult>
<Address>
<HoldingId xsi:nil="true"/>
<MainId>1617931</MainId>
<ContactId>8</ContactId>
<Description>Home, All Purposes</Description>
<Position/>
<Department/>
<Organisation/>
<AddressLabel>Mr Joe Bloggs</AddressLabel>
<AddressLine1>1 Fake Road</AddressLine1>
<AddressLine2/>
<AddressLine3/>
<Town>Faketown</Town>
<CountyId>818</CountyId>
<PostCode>FA33 4KE</PostCode>
<CountryId>3</CountryId>
<Phone>01234567890</Phone>
<EvePhone/>
<Mobile/>
<Email>joe#bloggs.com</Email>
<Fax/>
<WWW/>
<AddressTypeId>1</AddressTypeId>
<IsBilling>true</IsBilling>
<IsMailing>true</IsMailing>
<IsDelivery>true</IsDelivery>
<IsInherited>false</IsInherited>
<GridN/>
<GridE/>
<Latitude/>
<Longitude/>
<CensationCode/>
<IsDeleted>false</IsDeleted>
<HoldingPersonalDetailsId xsi:nil="true"/>
<IsSynced>false</IsSynced>
<BeenProcessed>false</BeenProcessed>
<CountyName/>
<CountryName/>
<AddressTypeName>Home</AddressTypeName>
</Address>
</Address_ListResult>
</Address_ListResponse>
</soap:Body>
</soap:Envelope>
Code for accessing the XML content:
$xml = simplexml_load_string($result);
echo "Town: " . $xml->children('http://schemas.xmlsoap.org/soap/envelope/')->children('http://example.example.com/')->Address_ListResponse->Town;
The above code was based on a link posted by another StackOverFlow question: http://blog.preinheimer.com/index.php?/archives/172-SimpleXML,-Namespaces-Hair-loss.html
Any help would be appreciated.
Thanks.
Consider using the SOAP extension instead.
See the example in the PHP Manual on how to write a client.
An alternative would be to use Zend_Soap as a standalone component.
Turns out the answer I was looking for wasn't SimpleXML - or at least I couldn't get that to work.
What I have done is used the xml_parse_into_struct to create an array of values returned from the XML data: http://www.php.net/manual/en/function.xml-parse-into-struct.php

Categories