Cannot get attributes of nested SimpleXMLElement - php

Clearly I'm missing something simple.
I have the following xml string which I parse using simplexml_load_string:
$xmlString = '<root><Title>Heading Text</Title><Image><img src="https://image.com?id=123" alt="alt text" /></Image></root>';
$xml = simplexml_load_string($xmlString);
However, I cannot access the img tag inside Image.
I would think I would use $xml->Image[0]->img to get the element and
$xml->Image[0]->img['src'] to get the url of the image. But I keep getting the error:
Trying to get property 'img' of non-object
$xml->Image[0] tests out as type SimpleXMLElement, and when I print_r() I get:
SimpleXMLElement Object (
[img] => SimpleXMLElement Object (
[#attributes] => Array (
[src] => https://image.com?id=123
[alt] => alt text
)
)
)
Like I said, I know I'm missing something really obvious, so any help would be appreciated.

You are correct, $xml->Image[0]->img['src'] will give you the src attribute, but it will give it to you as an object.
If you run print_r($xml->Image[0]->img['src']); it will show you this:
SimpleXMLElement Object
(
[0] => https://image.com?id=123
)
But if you run echo $xml->Image[0]->img['src']; instead, it will give you this:
https://image.com?id=123
The reason is that the SimpleXMLElement class implements the magic overload method __toString (or its equivalent in internal C code), so that whenever you cast the object to string, it will give you the string contents. Since echo always needs a string, it does this implicitly, but you can do it explicitly with (string), e.g.:
$imageSrc = (string)$xml->Image[0]->img['src'];
var_dump($imageSrc);
(As a side-note, the [0] is always optional - if you don't give a number, SimpleXML assumes you want the only or first child with that name, so $xml->Image->img['src'], $xml->Image[0]->img['src'], $xml->Image->img[0]['src'] and $xml->Image[0]->img[0]['src'] will all give the same result.)

Related

SimpleXML keeps returning content on CDATA element

So another CDATA returning content question. I've seen many answers, but even though I tried them all, I still get only content.
In more details:
I have an xml file (containing many NewsItem inside):
<NewsML>
<NewsItem>
<NewsComponent>
<ContentItem>
<DataContent>
<body>
<body.content>
<![CDATA[<p>This is what I am trying to retrieve</p>]]>
</body.content>
</body>
</DataContent>
</ContentItem>
</NewsComponent>
</NewsItem>
I am trying to get the content of body.content.
Here is my code:
$xml = simplexml_load_file('path/to/my/xml.xml',null,LIBXML_NOCDATA);
if(count($xml->children()) > 0){
foreach($xml->children() as $element){
$description = (string)$element->NewsComponent->ContentItem->DataContent->body->body.content;
echo $description;
}
}
echo '<pre>';
print_r($xml);
echo '</pre>';
My echo returns:
content
even though I do see the content in the print_r of my xml, as we can see here:
SimpleXMLElement Object
(
[NewsItem] => Array
(
[0] => SimpleXMLElement Object
(
[NewsComponent] => SimpleXMLElement Object
(
[ContentItem] => Array
(
[0] => SimpleXMLElement Object
(
[DataContent] => SimpleXMLElement Object
(
[body] => SimpleXMLElement Object
(
[body.content] => This is what I am trying to retieve
)
)
)
)
)
)
I tried using (string) or not on the element.
I also tried using
$xml = simplexml_load_file('path/to/my/xml.xml',null,LIBXML_NOCDATA);
vs
$xml = simplexml_load_file('path/to/my/xml.xml',"SimpleXMLElement",LIBXML_NOCDATA);
vs
$xml = simplexml_load_file('path/to/my/xml.xml');
For element names which cannot be PHP identifiers (like body.content), you must use an alternative PHP notation:
$element->NewsComponent->ContentItem->DataContent->body->{'body.content'};
I think your example returns 'content' because you are concatenating an element that does not exist
$element->NewsComponent->ContentItem->DataContent->body->body
with the string 'content' - probably PHP complains that there's no constant with the name content and therefore assumes you meant 'content'.
Thus my guess is you need to find another way to select an element with a dot in the name.
(This problem does not appear to be related to CDATA.)

Reading XML and storing to arrays in PHP

I have this:
$xml = simplexml_load_file('test.xml');
print"<pre>";
print_r($xml);
It printout this:
SimpleXMLElement Object
(
[b] => SimpleXMLElement Object
(
[c] => SimpleXMLElement Object
(
[d] => 543
)
)
)
but when I type echo $xml["b"]["c"]["d"]; nothing happens
the print_r is kind of misleading,
actually the $xml is series/array of SimpleXmlElement objects
so
echo (int)$xml->b->c->d; --- type casting is required
here is some reference you should take a look first
Additional to type casting,
because everything node inside the xml object is either string or int
PHP will auto convert for numeric string to integer,
however, is clearer if you provide the type hinting
var_dump($xml); --- you should see more information on the data type
Try echo $xml->b->c->d;
$xml is not array they are objects.

Getting value from SimpleXMLElement Object

Hi I have this following segment of XML:
......
<Result number="4" position="1" points="25">
<Driver driverId="button" url="http://en.wikipedia.org/wiki/Jenson_Button">
<GivenName>Jenson</GivenName>
<FamilyName>Button</FamilyName>
<DateOfBirth>1980-01-19</DateOfBirth>
<Nationality>British</Nationality>
</Driver>
......
I can use the following easily to get the GivenName:
$item->Driver->GivenName;
But when I use:
$item->Driver->FamilyName;
I get SimpleXMLElement Object ()
I have looked around and found that it might be something to do with passing it to a string but then I get nothing on screen. Not even SimpleXMLElement Object.
I don't understand as it's a sibling of GivenName and that works.
You get a SimpleXMLElement object in both cases, which you'll see if you use print_r():
print_r ($item->Driver->GivenName);
print_r ($item->Driver->FamilyName);
Outputs:
SimpleXMLElement Object
(
[0] => Jenson
)
SimpleXMLElement Object
(
[0] => Button
)
You can use an explicit cast to get the values as strings:
$givenNameString = (string) $item->Driver->GivenName;
$familyNameString = (string) $item->Driver->FamilyName;
To make PHP understand you have to give typecasting forcefully on object like below:
$givenName = (array) $item->Driver->GivenName;
$familyName = (array) $item->Driver->FamilyName;
print_r($givenName);
print_r($familyName);
OUTPUT :
Array ([0] => 'Jenson')
Array ([0] => 'Button')

Extract data from an XML object

How do i extract the data from that xml object which is a value of a certain array:
Array ( [Title] => SimpleXMLElement Object (
[0] => The Key of Life; A Metaphysical Investigation )
[ASIN] => SimpleXMLElement Object ( [0] => 0982385099 ) ...
Do string typecasting of the object.
$variable = (string) $FieldValue[0];
That would work, as SimpleXml has all the children in object type and not string.
say $X is the object, so to print
The Key of Life; A Metaphysical
Investigation
you do:
echo $X->Title[0]
It's important to understand that you're not working with an array — you're working with a SimpleXMLElement object, which is not the same.
Instead of doing $array['key']['subkey'], you would do $xml->tag->subtag.
SimpleXML nodes are not strings or arrays, although they behave string-like and array-like. Make sure you always typecast the value to an explicit string.
If you're accessing the first node, you don't need to use [0]. It's assumed.
You can convert SimpleXMLElement objects into true associative arrays in PHP 5.2 or newer with:
$array = json_decode(json_encode($xml), true);

Can I use SimpleXML & Xpath to directly select an Elements Attribute?

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.

Categories