SimpleXML Returns 0 Element - php

I'm taking my response from a Soap Request, and passing it into a new SimpleXML construct.
$response = $this->client->$method(array("Request" => $this->params));
$response_string = $this->client->__getLastResponse();
$this->response = new Classes_Result(new SimpleXMLElement($result));
If I echo the $response_string, it outputs a proper xml string. Here is a snippet as it's quite long.
<?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><GetClassesResponse xmlns="http://clients.mindbodyonline.com/api/0_5">
<GetClassesResult>
<Status>Success</Status>
<XMLDetail>Full</XMLDetail>
<ResultCount>6</ResultCount>
<CurrentPageIndex>0</CurrentPageIndex>
<TotalPageCount>1</TotalPageCount>
<Classes>
<Class>
<ClassScheduleID>4</ClassScheduleID>
<Location>
<SiteID>20853</SiteID>
....</soap:Envelope>
Hoever, when I try to work with this object, I get errors or if I dump the object it outputs:
object(SimpleXMLElement)#51 (0)
Any ideas why this might be happening?

You are not actually using $response_string, and you have not set $result anywhere, which you have passed to new SimpleXMLElement($result).
Perhaps you intend to build a SimpleXML object with the $response_string string via simplexml_load_string()?
$response = $this->client->$method(array("Request" => $this->params));
$response_string = $this->client->__getLastResponse();
// Load XML via simplexml_load_string()
$this->response = new Classes_Result(simplexml_load_string($response_string));
// Or if you do expect a SimpleXMLElement(), pass in the string
$this->response = new Classes_Result(new SimpleXMLElement($response_string));
The <soap:Body> element of your SOAP response is namespaced with (soap). To loop over it with SimpleXML, you must provide the correct namespace:
// After creating new SimpleXMLElement()
var_dump($this->response->children("http://schemas.xmlsoap.org/soap/envelope/"));
// class SimpleXMLElement#2 (1) {
// public $Body =>
// class SimpleXMLElement#4 (0) {
// }
// }
To loop over the body:
foreach ($this->response->children("http://schemas.xmlsoap.org/soap/envelope/") as $b) {
$body_nodes = $b->children();
// Get somethign specific
foreach ($body_nodes->GetClassesResponse->GetClassesResult as $bn) {
echo $bn->ResultCount . ", ";
echo $bn->TotalPageCount;
}
}
// 6, 1

Related

Parsing a SOAP response

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 = &apos;1.0&apos; encoding = &apos;UTF-8&apos;?>
<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;

Specific field from XML into PHP

I am pretty new to XML sheets in combination with PHP. I am trying to pull data from an XML file that is being returned to me via SOAP call.
My XML is being returned as this.
commented out some of the details
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<loginResponse>
<result>
<metadataServerUrl>https://...-api.salesforce.com/service...</metadataServerUrl>
<passwordExpired>false</passwordExpired>
<sandbox>false</sandbox>
<serverUrl>https://...--api.salesforce.com/services/Soap/u/21.0/0...</serverUrl>
<sessionId>....</sessionId>
<userId>....</userId>
<userInfo>
<accessibilityMode>false</accessibilityMode>
<currencySymbol>€</currencySymbol> ... </userInfo>
</result>
</loginResponse>
</soapenv:Body>
</soapenv:Envelope>
So I am trying to pull out of this the sessionID
// UP HERE SOAP CALL --- return data
.......
} else {
$response = curl_exec($soap_do);
curl_close($soap_do);
// print($response); <-- see result XML
// grabbing the sessionid
$xmlresponse = new SimpleXMLElement($response);
$test = $xmlresponse->result->sessionId['value'];
echo $test;
}
This returns blank, but when I start adding the LoginResponse and the Soapenv (body and envelope), i get an error about that I am trying to get a propperty of non-object. I am not sure what I am doing wrong here.
With SimpleXML you can use SimpleXMLElement::children to find children by an XML namespace (here soapenv).
For your case it would something like
$xmlresponse = new SimpleXMLElement($response);
$response = $xmlresponse->children('soapenv', true)->Body->children('', true)->loginResponse->result->sessionId;
var_dump($response);
Which results in
object(SimpleXMLElement)#4 (1) {
[0]=>
string(4) "...."
}
I want to say that you should use SoapClient(http://php.net/manual/tr/class.soapclient.php) for Soap Calls but if you don't want to use it, here is how you can parse this XML :
$xmlresponse = new SimpleXMLElement(str_ireplace([':Envelope', ':Body'], '', $response));
$test = $xmlresponse->soapenv->loginResponse->result->sessionId['value'];
echo $test;

Can't get data from XML with namespaces

I make a soap request and I get the following response:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://*************/******/****" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://*************/******/****/***">
<SOAP-ENV:Body>
<ns1:GetEventV2Response>
<ns1:GetEventV2Result>
<ns1:Events>
<ns1:Event>
<ns1:Id>147624</ns1:Id>
<ns1:Name>Rockstars</ns1:Name>
<ns1:Genre>
...
I have tried to get the element ID inside of <ns1:Event>, but the code bellow doesn't work and don't know why. I searched and tried a fews solutions but without success.
header("Content-type: text/xml");
....
$response = curl_exec($ch);
curl_close($ch);
x = new SimpleXmlElement($response);
foreach($x->xpath('//ns1:Event') as $event) {
var_export($event->xpath('ns1:Id'));
}
why don't you try this?
$xml = simplexml_load_string($xml);
$xml->registerXPathNamespace('soap', 'http://schemas.xmlsoap.org/soap/envelope/');
foreach ($xml->xpath('//ns1:Event') as $item)
{
print_r($item);
}
http://php.net/manual/en/function.simplexml-load-string.php
Use SimpleXMLElement::registerXPathNamespace to register the namespace. For example:
$x->registerXPathNamespace('ns1', 'http://*************/******/****');
and, later:
$event->registerXPathNamespace('ns1', 'http://*************/******/****');

Change format of the output XML , generated from PHP SoapClient?

I am trying to create this :
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<AccessKey xmlns="http://eatright/membership" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Value>67a4ef47-9ddf-471f-b6d0-0000c28d57d1</Value>
</AccessKey>
</s:Header>
<s:Body>
<WebUserLogin xmlns="http://eatright/membership">
<loginOrEmail>1083790</loginOrEmail>
<password>thomas</password>
</WebUserLogin>
</s:Body>
</s:Envelope>
I created this PHP code
class ChannelAdvisorAuth
{
public $AccessKey ;
public function __construct($key)
{
$this->AccessKey = $key;
}
}
$AccessKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$url = "http://ws.eatright.org/service/service.svc?wsdl";
$client = new SoapClient($url, array("trace" => 1, "exception" => 0));
$auth = new ChannelAdvisorAuth($AccessKey);
$header = new SoapHeader("AccessKey", "Value", $AccessKey, false);
$client->__setSoapHeaders($header);
$result = $client->ValidateAccessKey();
echo "REQUEST:\n" . $client->__getLastRequest() . "\n";
The output of the above PHP code is :
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://eatright/membership" xmlns:ns2="AccessKey">
<SOAP-ENV:Header>
<ns2:Value>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</ns2:Value>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:ValidateAccessKey/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
How to change the PHP code to output the XML as requested by web service provider?
How to replace the "SOAP-ENV" to "S"?
Is there is a way to remove the NS1 and NS2? and also adjust the whole format of the XML to meet the requirements? thanks
You don't need to worry about SOAP-ENV, ns1 or ns2 - they are just prefixes referring to the namespaces. As long as the full namespaces are correct, it's going to be alright.
I think the SOAP header should be made like this:
$access_key = new stdClass();
$access_key->Value = 'XXX';
$hdr = new SoapHeader('http://eatright/membership', 'AccessKey', $access_key);
$client->__setSoapHeaders($hdr);
I don't see a purpose of xmlns:i in the first example - there are no elements having XSI attributes.
I'm not sure what to do with the body. In your first example, there is a call to the WebUserLogin operation, while in your PHP code you are trying to call ValidateAccessKey.
Have you tried reading the WSDL file which is pointed by $url
Ok I found the problem and I will add it here in case someone looking for same issue.
$access_key = new stdClass();
$access_key->Value = 'xxxxxxxxxxxxxxxxxxx';
// Create the SoapClient instance
$url = "http://ws.eatright.org/service/service.svc?wsdl";
$client = new SoapClient($url, array("trace" => 1, "exception" => 0));
$hdr = new SoapHeader('http://eatright/membership', 'AccessKey', $access_key);
$client->__setSoapHeaders($hdr);
$soapParameters = array('loginOrEmail ' => $username, 'password' => $password);
$login = new stdClass();
$login->loginOrEmail='LoginID';
$login->password='Password';
$result = $client->WebUserLogin($login);

PHP - SimpleXML - AddChild with another SimpleXMLElement

I'm trying to build a rather complex XML document.
I have a bunch of sections of the XML document that repeats. I thought I'd use multiple string templates as base document for the sections and create instances of XML elements using simplexml_load_string.
So I have one instance of SimpleXMLElement as the base document
$root =
simplexml_load_string($template_root);
then I loop through some items in my database, create new SimpleXMLElement, something like this:
for (bla bla bla):
$item = simplexml_load_string($template_item);
// do stuff with item
// try to add item to the root document..
// Stuck here.. can't do $root->items->addChild($item)
endfor;
I can't call addChild because it just expects a tag name and value.. you can't addChild another SimpleXMLElement.
Am I missing something here? seems really dumb that addChild can't take a SimpleXMLELement as a parameter.
Is there any other way to do this? (apart from using a different xml lib)
As far as I know, you can't do it with SimpleXML because addChild doesn't make a deep copy of the element (being necessary to specify the tag name can easily be overcome by calling SimpleXMLElement::getName()).
One solution would be to use DOM instead:
With this function:
function sxml_append(SimpleXMLElement $to, SimpleXMLElement $from) {
$toDom = dom_import_simplexml($to);
$fromDom = dom_import_simplexml($from);
$toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true));
}
We have for
<?php
header("Content-type: text/plain");
$sxml = simplexml_load_string("<root></root>");
$n1 = simplexml_load_string("<child>one</child>");
$n2 = simplexml_load_string("<child><k>two</k></child>");
sxml_append($sxml, $n1);
sxml_append($sxml, $n2);
echo $sxml->asXML();
the output
<?xml version="1.0"?>
<root><child>one</child><child><k>two</k></child></root>
See also some user comments that use recursive functions and addChild, e.g. this one.
You could use this function that is based in creating the children with attributes from the source:
function xml_adopt($root, $new) {
$node = $root->addChild($new->getName(), (string) $new);
foreach($new->attributes() as $attr => $value) {
$node->addAttribute($attr, $value);
}
foreach($new->children() as $ch) {
xml_adopt($node, $ch);
}
}
$xml = new SimpleXMLElement("<root/>");
$child = new SimpleXMLElement("<content><p a=\"aaaaaaa\">a paragraph</p><p>another <br/>p</p></content>");
xml_adopt($xml, $child);
echo $xml->asXML()."\n";
This will produce:
<?xml version="1.0"?>
<root><content><p a="aaaaaaa">a paragraph</p><p>another p<br/></p></content></root>
The xml_adopt() example doesn't preserve namespace nodes.
My edit was rejected because it changed to much? was spam?.
Here is a version of xml_adopt() that preserves namespaces.
function xml_adopt($root, $new, $namespace = null) {
// first add the new node
// NOTE: addChild does NOT escape "&" ampersands in (string)$new !!!
// replace them or use htmlspecialchars(). see addchild docs comments.
$node = $root->addChild($new->getName(), (string) $new, $namespace);
// add any attributes for the new node
foreach($new->attributes() as $attr => $value) {
$node->addAttribute($attr, $value);
}
// get all namespaces, include a blank one
$namespaces = array_merge(array(null), $new->getNameSpaces(true));
// add any child nodes, including optional namespace
foreach($namespaces as $space) {
foreach ($new->children($space) as $child) {
xml_adopt($node, $child, $space);
}
}
}
(edit: example added)
$xml = new SimpleXMLElement(
'<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
<channel></channel></rss>');
$item = new SimpleXMLElement(
'<item xmlns:media="http://search.yahoo.com/mrss/">
<title>Slide Title</title>
<description>Some description</description>
<link>http://example.com/img/image.jpg</link>
<guid isPermaLink="false">A1234</guid>
<media:content url="http://example.com/img/image.jpg" medium="image" duration="15">
</media:content>
</item>');
$channel = $xml->channel;
xml_adopt($channel, $item);
// output:
// Note that the namespace is (correctly) only preserved on the root element
'<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
<item>
<title>Slide Title</title>
<description>Some description</description>
<link>http://example.com/img/image.jpg</link>
<guid isPermaLink="false">A1234</guid>
<media:content url="http://example.com/img/image.jpg" medium="image" duration="15">
</media:content>
</item>
</channel>
</rss>'

Categories