Get Attribute value from Soap Response PHP - php

I am getting a soap response as expected and then converting to an array. Here is my code:
$response = $client->__getLastResponse();
$response = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $response);
$xml = new SimpleXMLElement($response);
$body = $xml->xpath('//soapBody')[0];
$array = json_decode( str_replace('#', '', json_encode((array)$body)), TRUE);
print_r($array);
here is the output:
Array (
[GetCompanyCodeResponse] => Array (
[GetCompanyCodeResult] => Array (
[Customers] => Array (
[Customer] => Array (
[attributes] => Array (
[CustomerNo] => 103987
[CustomerName] => epds api testers Inc
[ContactId] => 219196
)
)
)
)
)
How do i echo the ContactId? Ive tried the following:
$att = $array->attributes();
$array->attributes()->{'ContactId'};
print_r($array);
I get the following error:
Fatal error: Uncaught Error: Call to a member function attributes() on array
Also tried:
$array->Customer['CustomerId'];
I get following error:
Notice: Trying to get property 'Customer' of non-object
Expecting to get 219196

I found the solution to the above problem. Not sure if it is the most elegant way to do it, but it returns result as expected. If there is a more efficient way to get the ContactId, I am open to suggestions.
print_r($array['GetCompanyCodeResponse']['GetCompanyCodeResult']
['Customers']['Customer']['attributes']['ContactId']);

You have followed some very bad advice on how to parse the XML, and completely thrown away the functionality of SimpleXML.
Specifically, the reason you can't run the attributes() method is that you've converted the SimpleXML object to a plain array using this ugly hack:
$array = json_decode( str_replace('#', '', json_encode((array)$body)), TRUE);
To use SimpleXML as its authors intended, I suggest you read:
The examples in the PHP manual
This reference answer on handling XML namespaces
Since you didn't paste the actual XML in the question, I'm going to take a guess that it looks like this:
<?xml version = "1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope">
<soap:Body xmlns="http://www.example.org/companyInfo">
<GetCompanyCodeResponse>
<GetCompanyCodeResult>
<Customers>
<Customer CustomerNo="103987" CustomerName="epds api testers Inc" ContactId="219196" />
</Customers>
</GetCompanyCodeResult>
</GetCompanyCodeResponse>
</soap:Body>
</soap:Envelope>
If that is in $response, we don't need to do any weirdness with str_replace or json_encode, we can use the methods built into SimpleXML to navigate around the XML:
$xml = new SimpleXMLElement($response);
// The Body is in the SOAP Envelope namespace
$body = $xml->children('http://www.w3.org/2001/12/soap-envelope')->Body;
// The element inside that is in some other namespace
$innerResponse = $body->children('http://www.example.org/companyInfo')->GetCompanyCodeResponse;
// We need to traverse the XML to get to the node we're interested in
$customer = $innerResponse->GetCompanyCodeResult->Customers->Customer;
// Unprefixed attributes aren't technically in any namespace (an oddity in the XML namespace spec!)
$attributes = $customer->attributes(null);
// Here's the value you were looking for
echo $attributes['ContactId'];
Unlike your previous code, this won't break if:
The server starts using a different local prefix instead of soap:, or adding a prefix on the GetCompanyCodeResponse element
The response comes back with more than one Customer (the ->Customer always means the same as ->Customer[0], the first child element with that name)
The Customer element has child elements or text content as well as attributes
It also allows you to use other features of SimpleXML, like using an xpath expression to search the document or even switching to the full DOM API for more complex operations.

Related

Getting Exception: String could not be parsed as XML when calling SimpleXMLElement

I am trying to parse this XML (http://numismatics.org/search/apis/getNuds?identifiers=1995.11.282) and pull out the elements under but when I call SimpleXMLElement I get: "Exception: String could not be parsed as XML" (the API is from http://numismatics.org/search/apis)
Here is what the XML basically looks like from the link above:
<nuds xmlns="http://nomisma.org/nuds" xmlns:mets="http://www.loc.gov/METS/" xmlns:tei="http://www.tei-c.org/ns/1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://nomisma.org/nuds http://nomisma.org/nuds.xsd" recordType="physical">
...deleted to shorten question
<digRep>
<mets:fileSec>
<mets:fileGrp USE="obverse">
<mets:file USE="iiif">
<mets:FLocat LOCYPE="URL" xlink:href="http://images.numismatics.org/collectionimages%2F19501999%2F1995%2F1995.11.282.obv.noscale.jpg"/>
</mets:file>
...deleted to shorten question
</mets:fileGrp>
</mets:fileSec>
</digRep>
</nuds>
Here is the test code I'm using from this example
$detail = simplexml_load_file("http://numismatics.org/search/apis/getNuds?identifiers=1995.11.282");
$digRep = new \SimpleXMLElement($detail->nuds->digRep); //the slash before the SimpleXMLElement is required for Laravel
$digRep->registerXPathNamespace('c', 'http://www.loc.gov/METS');
$result = $digRep->xpath('//c:mets:fileSec');
foreach ($result as $title) {
echo $title . "\n";
}
I am wondering if this has to do with "mets:fileSec" but I am not quite sure what "fileSec" is in this context.
mets is just the alias for the namespace http://www.loc.gov/METS/ (the xmlns:mets definition). This is perfectly fine XML and it parses in the browser if called directly. I suggest checking if you really can get the XML with PHP (with file_get_contents()) and not some error message. Services return sometimes different result for different user agents.

How to get name of very first tag of XML with php's SimpleXML?

I am parsing XML strings using simplexml_load_string(), but I noticed that i don't get the name of the very first tag.
For example, I have these two xml strings:
$s = '<?xml version="1.0" encoding="UTF-8"?>
<ParentTypeABC>
<chidren1>
<children2>1000</children2>
</chidren1>
</ParentTypeABC>
';
$t = '<?xml version="1.0" encoding="UTF-8"?>
<ParentTypeDEF>
<chidren1>
<children2>1000</children2>
</chidren1>
</ParentTypeDEF>
';
NOTICE that they are nearly identical, the only difference being that one has the first node as <ParentTypeABC> and the other as <ParentTypeDEF>
then I just convert them to SimpleXML objects:
$o = simplexml_load_string($s);
$p = simplexml_load_string($t);
but then i have two equal objects, none of them having the "top" node's name appearing, either ParentTypeABC or ParentTypeDEF (I examine the objects using print_r()):
// with top node "ParentTypeABC"
SimpleXMLElement Object
(
[chidren1] => SimpleXMLElement Object
(
[children2] => 1000
)
)
// with top node "ParentTypeDEF"
SimpleXMLElement Object
(
[chidren1] => SimpleXMLElement Object
(
[children2] => 1000
)
)
So how I am supposed to know the top node's name? If I parse unknown XMLs and I need to know what's the top node name, what can I do?
Is there an option in simplexml_load_string() I could use?
I know there are MANY ways to parse XML's with PHP, but I'd like it to be as simple as posible, and to get a simple object or array I could navigate easily.
I made a simple example here to fiddle with.
SimpleXML has a getName() method.
echo $xml->getName();
This should return the name of the respective node, no matter if root or not.
http://php.net/manual/en/simplexmlelement.getname.php

PHP SimpleXML Element parsing issue

I've come across a weird but apparently valid XML string that I'm being returned by an API. I've been parsing XML with SimpleXML because it's really easy to pass it to a function and convert it into a handy array.
The following is parsed incorrectly by SimpleXML:
<?xml version="1.0" standalone="yes"?>
<Response>
<CustomsID>010912-1
<IsApproved>NO</IsApproved>
<ErrorMsg>Electronic refunds...</ErrorMsg>
</CustomsID>
</Response>
Simple XML results in:
SimpleXMLElement Object ( [CustomsID] => 010912-1 )
Is there a way to parse this in XML? Or another XML library that returns an object that reflects the XML structure?
That is an odd response with the text along with other nodes. If you manually traverse it (not as an array, but as an object) you should be able to get inside:
<?php
$xml = '<?xml version="1.0" standalone="yes"?>
<Response>
<CustomsID>010912-1
<IsApproved>NO</IsApproved>
<ErrorMsg>Electronic refunds...</ErrorMsg>
</CustomsID>
</Response>';
$sObj = new SimpleXMLElement( $xml );
var_dump( $sObj->CustomsID );
exit;
?>
Results in second object:
object(SimpleXMLElement)#2 (2) {
["IsApproved"]=>
string(2) "NO"
["ErrorMsg"]=>
string(21) "Electronic refunds..."
}
You already parse the XML with SimpleXML. I guess you want to parse it into a handy array which you not further define.
The problem with the XML you have is that it's structure is not very distinct. In case it does not change much, you can convert it into an array using a SimpleXMLIterator instead of a SimpleXMLElement:
$it = new SimpleXMLIterator($xml);
$mode = RecursiveIteratorIterator::SELF_FIRST;
$rit = new RecursiveIteratorIterator($it, $mode);
$array = array_map('trim', iterator_to_array($rit));
print_r($array);
For the XML-string in question this gives:
Array
(
[CustomsID] => 010912-1
[IsApproved] => NO
[ErrorMsg] => Electronic refunds...
)
See as well the online demo and How to parse and process HTML/XML with PHP?.

file_get_contents() warning when load xml data

what i want to do is : i have an CRM API which produce data in xml format,
i am using DOMDocument() to fetch the data into pieces. The problem is when i tried to load the data like : $dom->loadXML($data);
i got the error :
DOMDocument::loadXML() [domdocument.loadxml]: Input is not proper UTF-8
i google i got an alternative solution to use with file_get_contents() but still i am getting warning like:
here is the code that i am using to fetch the xml data:
$text = file_get_contents(stripslashes(utf8_decode($data)));
$doc = new DOMDocument();
$doc->loadXML($text);
and here i put my XML return data, not complete but just a small piece:
<?xml version="1.0" encoding="utf-8"?>
<whmcsapi version="5.0.3">
<action>getticket</action>
<result>success</result>
<ticketid>5767</ticketid>
<tid>409865</tid>
<c>NwLldOG6</c>
<deptid>2</deptid>
<deptname>Technical</deptname>
<userid>27476</userid>
<name>shirley b broyles (home)</name>
<email>sbroyles1#stx.rr.com</email>
<cc></cc>
<date>2012-08-27 19:52:12</date>
<subject>printer not working</subject>
<status>Customer-Reply</status>
<priority>High</priority>
<admin></admin>
<lastreply>2012-08-28 23:34:17</lastreply>
<flag>0</flag>
<service></service>
<replies>
<reply>
can anyone please tell me where is the problem? or any alternative ??
Okay, so let's strip down the problem. If $data contains invalid UTF-8, you should be thinking how to make it valid; one way (not sure if that works for you) is with utf8_encode() rather than utf8_decode() (which is used to turn UTF-8 into ISO-8859-1):
$doc = new DOMDocument();
$doc->loadXML(utf8_encode($data));
Otherwise you will need to find out which part of the text includes the bad input. I'll see what I can come up with.
Resources
Ensuring valid utf-8 in PHP
you can try simplexml_load_file php function..
$xml = simplexml_load_file("addinsrss.xml");
echo "<pre>";print_r($xml);echo "</pre>";
If you already have the xml source then dont pass it to FGC. Pass it directly to domDocument.
Or you can also use simplexml_load_string(),
<?php
$xml = '<?xml version="1.0" encoding="utf-8"?>
<whmcsapi version="5.0.3">
<action>getticket</action>
<result>success</result>
<ticketid>5767</ticketid>
<tid>409865</tid>
<c>NwLldOG6</c>
<deptid>2</deptid>
<deptname>Technical</deptname>
<userid>27476</userid>
</whmcsapi>
';
$xml = simplexml_load_string($xml);
print_r($xml);
/*
SimpleXMLElement Object
(
[#attributes] => Array
(
[version] => 5.0.3
)
[action] => getticket
[result] => success
[ticketid] => 5767
[tid] => 409865
[c] => NwLldOG6
[deptid] => 2
[deptname] => Technical
[userid] => 27476
...
...
)
*/
echo $xml->tid; //409865
?>
Or directly use $xml = simplexml_load_file('http://example.com/somexml.xml'); to load from an external url.

Using xpath on a PHP SimpleXML object, SOAP + namespaces (not working..)

After researching this on SO and google for hours now... I hope to get some help here:
(I am just one step away from running a regex to remove the namespaces completely)
First this is the XML:
<?xml version="1.0" encoding="utf-16"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header xmlns="http://webservices.site.com/definitions">
<SessionId>0119A|1</SessionId>
</soap:Header>
<soap:Body>
<Security_AuthenticateReply xmlns="http://xml.site.com/QQ">
<processStatus>
<statusCode>P</statusCode>
</processStatus>
</Security_AuthenticateReply>
</soap:Body>
</soap:Envelope>
Now this is what my code in PHP looks like:
$response = simplexml_load_string( $str ,NULL,
false, "http://schemas.xmlsoap.org/soap/envelope/" );
// just making sure the name space is "registered"
// but I tested all examples also with this removed
$response->registerXPathNamespace("soap",
"http://schemas.xmlsoap.org/soap/envelope/");
$_res = $response->xpath('//soap:Header');
print_r($_res);
/*** result: simple query for the root "soap" namespace, this looks good! (so far..)
Array
(
[0] => SimpleXMLElement Object
(
[SessionId] => 0119A|1
)
)
***/
// now we query for the "SessionId" element in the XML
$_res = $response->xpath('//soap:Header/SessionId');
print_r($_res);
/*** result: this does not return anything!
Array
(
)
***/
// another approach
$_res = $response->xpath('//soap:Header/SessionId/text()');
print_r($_res);
/*** result: this does not return anything at all!
***/
// Finally, without using XPath this does work
$_res = $response->xpath('//soap:Header');
$_res = (string)$_res[0]->SessionId;
echo $_res;
/*** result: this worked
0119A|1
***/
How can I get the SOAP message working with XPATH???
Thanks,
Roman
The multiple namespaces are messing with it, adding the following works for me
$response->registerXPathNamespace("site", "http://webservices.site.com/definitions");
$_res = $response->xpath('//site:SessionId');
also, see this previous stack overflow question
You need to register the default namespace used by <SessionId> element as well. Because <SessionId> is in the default namespace it does not have any prefix but in order to your XPath to work, you need to bind also this namespace to some prefix and then use that prefix in your XPath expression.
$response->registerXPathNamespace("ns",
"http://webservices.site.com/definitions");
$_res = $response->xpath('//soap:Header/ns:SessionId');
XPath (1.0) expressions without a namespace prefix always match only to targets in no-namespace.

Categories