I have the following SimpleXMLElement Object http://pastebin.com/qEP0UUPJ
I can query it using xpath $xaugbp = $xml->xpath("/query/results/rate"); but this just narrows down the search:
http://pastebin.com/Xwezx4bZ
I wish to access the following object:
[0] => SimpleXMLElement Object
(
[#attributes] => Array
(
[id] => XAUGBP
)
[Name] => XAU to GBP
[Rate] => 1030.5784
[Date] => 7/27/2012
[Time] => 5:55pm
[Ask] => 1027.5662
[Bid] => 1033.5896
I can get that particular object using
$ob = $xmlObject->xpath("//query/results/rate[contains(#id, 'XAUGBP')]");
I can get 'Ask' by using
$ob = $xmlObject->xpath("//query/results/rate[contains(#id, 'XAUGBP')]/Ask");
But how do I turn that object into a variable. e.g. so that I can do calculations on it?
Is xpath the only way of doing this? Is there a better / more efficient / quicker way? Is it possible to convert this to an associative array for easy access?
Preface
Before you do anything, (re)familiarise yourself with SimpleXML's peculiar way of doing things. The best place to start is the SimpleXML basic usage manual page.
The example below shows you:
Getting a single <rate> element object based on an attribute's value, and how to display it.
Looping over all of the <rate> elements and displaying their values.
Basic example code
$query = simplexml_load_file('…');
// Get a <rate> element by its id attribute
$rates = $query->xpath('results/rate[#id = "XAUGBP"]');
$rate = $rates[0];
echo "$rate->Name was at $rate->Rate at $rate->Time on $rate->Date\n";
echo "-----\n";
// or, loop over all of the <rate> elements
foreach ($query->results->rate as $rate) {
echo "$rate->Name was at $rate->Rate at $rate->Time on $rate->Date\n";
}
The above example outputs:
XAU to GBP was at 1030.5784 at 5:55pm on 7/27/2012
-----
XAU to GBP was at 1030.5784 at 5:55pm on 7/27/2012
XAG to GBPZ 999 N was at 17.4502 at 5:55pm on 7/27/2012
XPT to GBPZ 999 was at 893.3414 at 5:55pm on 7/27/2012
XPD to GBP1 UZ was at 362.652 at 5:55pm on 7/27/2012
(See this example running online)
XML to array
I see this being asked time and time and time again. Rather than turning a tree of SimpleXMLElement objects into some "friendly" array, please instead take the time to learn how to use SimpleXML: once you have done that, there is no need to take the intermediate step of turning the objects into arrays to work with. The "basic usage" page referred to above should get you going, but key points to know are:
Child nodes use property syntax: $parent->child
Attributes use array-style syntax: $element["attr_name"]
Cast objects into friendly types when desired: (string) $element->child
XPath query should be "//query/results/rate[contains(#id, 'XAUGBP')]"
Related
I am working on a project to get the names of an array.
The arrays seem to be multidimensional, with the added bonus of being a stdclass Object. I am trying to select a key from the provided array but seem to have no luck selecting them.
echo($response->array[shoecompany]->array[1]->name);
from the information here
stdClass Object
(
[shoe] => shoemaker
[shoecompany] => Array
(
[0] => stdClass Object
(
[shoenumber] => 1
[name] => Blank
[1] => stdClass Object
(
[shoenumber] => 2
[name] => demo
)
[2] => stdClass Object
(
[shoenumber] => certificate
[name] => certofsale
)
)
)
Nothing i do seems to pull the information i need out of this. Any ways to go about pulling, said information.
The arrays seem to be multidimensional, with the added bonus of being a stdclass Object.
Arrays and objects aren't the same things.
I let you learn more about the specifics of both if you are curious.
Regarding access, yous use brackets - '[]' - when you want to access something in an array and an arrow - '->' - when you want to access an object's property :
$array['key'];
$object->property;
In your case, since only $response and the entries in the entry showcompany - I assume it's a typo - are objects, what you should write is :
$response->shoecompany[1]->name;
Which gives you in practical use :
foreach ($response->shoecompany as $val) {
echo $val->shoenumber, ' : ', $val->name, '<br>'; // Or whatever you want to print, that's for the sake of providing an example
}
If it is more convenient for you to handle exclusively arrays, you can also use get_object_vars() to convert an object properties to an array :
$response = get_object_vars($response);
Code should be like:
echo $response->shoecomapny[1]->name;
In short, to select key inside an object you need to use "->" operator and to select key inside array use "[]".
I have a php stdObject that is an object, and sometimes an array of Objects.
EDIT: It's been returned by SoapClient::__soapCall.
If it has one entry:
stdClass Object
(
[Event] => stdClass Object
(
[Nr] => 7050
[Date] => 2016-11-30T00:00:00
)
)
If it's got multiple entries:
stdClass Object
(
[Event] => Array
(
[0] => stdClass Object
(
[Nr] => 7015
[Date] => 2016-04-28T00:00:00
)
[1] => stdClass Object
(
[Nr] => 7016
[Date] => 2016-04-29T00:00:00
)
[2] => stdClass Object
(
[Nr] => 7017
[Date] => 2016-06-08T00:00:00
)
)
)
I don't see why, and I'd like to correct it.
Here's how I got there:
$items = $this->getAll(); // returns an Object with n entries
foreach($items as $item){
$fullItem = $this->item->getElementByID($item->Nr); // returns the full data for this entry
$item->Days = $fullItem->Days; // that's the one that can contain objects or just the data if it has one entry
}
My Questions:
Is that normal, that the same request returns different data types?
Should I "normalize" this and make an array of objects if there's only one entry?
Or is there an other recommended way to cope with it?
I don't think it's ok in this case. You should keep the interface the same. So if you are expecting an unknown number of elements it should always be a collection, even if in some cases only one element is returned. However if you expect only one element then the collection should be omitted.
$this->getAll(); //should always return a collection
$this->getOneById($id); //should return single item without collection, return null or throw exception if no element is found
On the question should you "normalize" (refactor) it I wold say it depends on the effort estimation and the benefit you'll gain form the refactoring. If this occurs in few places only and it's not likely to be used in any other place in the future I think you shouldn't bother. In case you decide to refactor be very careful and ensure the whole application is working properly. If you have tests - great.
I didn't declare it in the question, sorry: The data is the result of a soap client call:
$this->soapClient = new \SoapClient($wsdlUrl, array('trace' => DEBUG_SOAP));
// ...
$result = $this->soapClient->__soapCall($method, array($method => $params));
I think what confused me is the standard behaviour of SoapClient::__soapCall
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.
http://php.net/manual/en/soapclient.soapcall.php
Also, a very similar question: PHP SoapClient type mapping behaves differently
And here are two solutions from there:
"Normalize" (what's the correct word?) the data AFTER we got it:
if (is_object($variable)){
$variable = array($variable);
}
https://stackoverflow.com/a/6408780/160968
And, that would be the equivalent to refactoring the request BEFORE we get it – there's a setting for the soapClient!
$soapConfig = array(
'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
'trace' => true
);
$client = new SoapClient($wsdlUrl, $soapConfig);
https://stackoverflow.com/a/8008715/160968
Awesome!
I am very basic new php learner, i having difficulty to get nested array value, here is my json result:
stdClass Object
(
[title] => Aao Raja - Gabbar Is Back | Chitrangada Singh
[link] => stdClass Object
(
[22] => Array
(
[0] => http://r8---sn-aigllnsk.c.docs.google.com/videoplayback?mime=video%2Fmp4&id=o-AExJcTxRDvCYsfgA1cIvQDs1v-pvLhKjTPdDh67X19vz&dur=145.542&itag=22&pl=48&ip=2a03:b0c0:1:d0::2f6:c001&sparams=dur,expire,id,ip,ipbits,itag,lmt,mime,mm,mn,ms,mv,nh,pl,ratebypass,source,upn&key=cms1&sver=3&expire=1437035009&upn=9lTw9Popb18&ratebypass=yes&source=youtube&lmt=1432539432699196&fexp=901816%2C9407809%2C9408142%2C9408420%2C9408710%2C9409172%2C9412774%2C9412846%2C9413149%2C9415664%2C9415958%2C9416126%2C9416370%2C9416656&ipbits=0&signature=3547894526817B37774A7838F8B68493CDD62101.3F143C74D76E8705800445A4CD4476C4F8BCD988&cms_redirect=yes&mm=31&mn=sn-aigllnsk&ms=au&mt=1437013301&mv=m&nh=IgpwcjAzLmxocjE0KgkxMjcuMC4wLjE&utmg=ytap1
[1] =>
[2] => hd720
)
[43] => Array
(
[0] => http://r8---sn-aigllnsk.c.docs.google.com/videoplayback?mime=video%2Fwebm&id=o-AExJcTxRDvCYsfgA1cIvQDs1v-pvLhKjTPdDh67X19vz&dur=0.000&itag=43&pl=48&ip=2a03:b0c0:1:d0::2f6:c001&sparams=dur,expire,id,ip,ipbits,itag,lmt,mime,mm,mn,ms,mv,nh,pl,ratebypass,source,upn&key=cms1&sver=3&expire=1437035009&upn=9lTw9Popb18&ratebypass=yes&source=youtube&lmt=1428933984759484&fexp=901816%2C9407809%2C9408142%2C9408420%2C9408710%2C9409172%2C9412774%2C9412846%2C9413149%2C9415664%2C9415958%2C9416126%2C9416370%2C9416656&ipbits=0&signature=266C126464ECDB4CC0FF076CD41F07BCC4DA7E34.08D9F13B7BF7D92FD1E1963336CC7FB8F19FE899&cms_redirect=yes&mm=31&mn=sn-aigllnsk&ms=au&mt=1437013301&mv=m&nh=IgpwcjAzLmxocjE0KgkxMjcuMC4wLjE&utmg=ytap1
[1] =>
[2] => medium
)
I can access the Title, but can't access the Link urls:
echo $title = $json->{'title'};
echo $link = $json->{'link'}->{'22'}->{'0'};
How can access the specific link array 22
This echo $title = $json->{'title'}; works because you are accessing an object's property and using -> is the correct way.
In this case $json->{'link'}->{'22'}->{'0'} you are trying to access an array item instead an object's property, because $json->{'link'}->{'22'} is an array and not an object. In this case, you should access it in this way: $json->{'link'}->{'22'}[0]. In order to avoid this kind of issues and, when you decode your JSON to a PHP object, you can pass true as a second parameter to the function json_decode and that will convert the whole object into an array. That way, you don't need to worry about accessing elements as object's attributes, you can access them, always, as array items. So, in this case, it would be: $json["link"]["22"][0].
You're confusing the way you access objects and arrays.
Getting the title is correct via $json->title, but the link should be $json->link->{'22'}[0] - a mixture of objects and arrays.
FYI the {'name'} notation is the same as name - only required when you are including variables in your object name e.g. {$someVar . 'name'}
I suppose you use json_decode() function. Do you know that you can get an array instead of StdClass Object? So, you can use.
<?php
$php_array = json_decode($json_string, true);
I am pretty new to PHP an XML and hope you can help me with this.
Searching the forum didn't help me yet to find an answer to my specific issue.
I have a PHP page with a simplexml array that looks like the following, just longer:
SimpleXMLElement Object
(
[textID] => Array
(
[0] => SimpleXMLElement Object
(
[textID] => 1
[content] => Text1
)
[1] => SimpleXMLElement Object
(
[textID] => 2
[content] => Text2
)
[2] => SimpleXMLElement Object
(
[textID] => 3
[content] => Text3
)
)
)
Now I am trying to echo out a specific value from this array by referring to its ID which is an integer.
The only way I get this working is the following but this just goes by the order within the array, not by the actual ID:
<?php echo $objTexts->textID[1]->content; ?>
Can someone tell me what I am missing here ?
Thanks, Tim
SimpleXML has no way of knowing that the textID identifies which node is which - it is just another element in the XML.
Based on your sample output, your XML is a little confusing as you have multiple elements called textID which each have a single child, also called textID, which has a different meaning. Nonetheless, what you want to do can be achieved either by looping through all the outer textID elements and testing the value of their inner textID element:
foreach ( $objTexts->textID as $item )
{
if ( $item->textID == '2' )
{
...
}
}
Or, you could use XPath, which is a fairly simple query language for XML, and is supported within SimpleXML in the form of the ->xpath() method. In your case, you want to find a textID node which contains a textID child with a particular value, so the code would look something like this:
// ->xpath always returns a plain PHP array - not a SimpleXML object
$xpath_results = $objTexts->xpath('//textID[textID=2]');
// If you're certain you only want the first result:
echo $xpath_results[0]->content;
// If you might want multiple matches
foreach ( $xpath_results as $item )
{
...
}
i.e. - i want to return a string "yellow" using something like xpath expression "//banana/#color" and the following example xml...
<fruits>
<kiwi color="green" texture="hairy"/>
<banana color="yellow" texture="waxy"/>
</fruits>
$fruits = simplexml_load_string(
'<fruits>
<kiwi color="green" texture="hairy"/>
<banana color="yellow" texture="waxy"/>
</fruits>');
print_r($fruits->xpath('//banana/#color'));
produces
Array
(
[0] => SimpleXMLElement Object
(
[#attributes] => Array
(
[color] => yellow
)
)
)
whereas i would prefer something like...
Array
(
[0] => SimpleXMLElement Object
(
[0] => yellow
)
)
...so that i don't need to write a special case into the application i'm writing.
thanks very much! :)
I just gave your test a shot because i was curious and I found that it does actually produce the string value yellow when converted to string.
$fruits = simplexml_load_string(
'<fruits>
<kiwi color="green" texture="hairy"/>
<banana color="yellow" texture="waxy"/>
</fruits>');
$found = $fruits->xpath('//banana/#color');
echo $found[0];
It would seem this is just how SimpleXmlElement attribute nodes are represented. So you can use this as (string) $found[0] if you are not printing/echoing it directly.
Of course if your depending on the value remaining a SimpleXMLElement then that could be an issue I suppose. But i would think just remembering to cast as string when you go to use the node later would still be doable.
IF you really need a detailed interface for Nodes that supports an Attribute as a node then you may want to just switch to DOMDocument. You code will get more verbose, but the implementation is more clear.