I cannot get XML node contents and attributes at the same time with SimpleXML library:
I have the following XML, and want to get content#name attribute and node's contents:
<page id="id1">
<content name="abc">def</content>
</page>
Method simplexml_load_string()
print_r(simplexml_load_string('<page id="id1"><content name="abc">def</content></page>'));
outputs this:
SimpleXMLElement Object
(
[#attributes] => Array
(
[id] => id1
)
[content] => def
)
As you can see, contents of the content node is present, but attributes are missing. How can I receive the contents and attributes?
Thanks!
The attributes of content are present. This is just a trick of print_r() and how it works with XML objects in memory.
$x = simplexml_load_string('<page id="id1"><content name="abc">def</content></page>');
print_r($x->content);
print_r($x->content['name']);
SimpleXMLElement Object
(
[#attributes] => Array
(
[name] => abc
)
[0] => def
)
SimpleXMLElement Object
(
[0] => abc
)
In simplexml, accessing elements returns SimpleXMLElement objects. You can view the content of these objects using var_dump.
$book=simplexml_load_string('<page id="id1"><content name="abc">def</content></page>');
$content=$book->content;
var_dump($content);
You can access these objects with foreach loop.
foreach($obj as $value) {
if (is_array($value)) {
foreach ($value as $name=>$value) {
print $name.": ".$value."\n";}
}
else print $value;
}
You can not only retrieve contents (such as elements and attributes) but also add and remove them. You can also use Xpath to navigate values in complex XML tree. You just need to go through the methods of SimpleXMLElement class here.
$x = simplexml_load_string('<page id="id1"><content name="abc">def</content></page>');
To get the node's attributes:
$attributes = $x->content->attributes(); //where content is the name of the node
$name = $attributes['name'];
To get the content node's content:
$c = $x->content;
Interesting, that $c can be used as string and as object, i.e.
echo $c; //prints string
print_r($c) //prints it out as object
Related
I have a XML object result from my database containing settings.
I am trying to access the values for a particular settingName:
SimpleXMLElement Object
(
[settings] => Array
(
[0] => SimpleXMLElement Object
(
[settingName] => Test
[settingDescription] => Testing
[requireValue] => 1
[localeID] => 14
[status] => 1
[value] => 66
[settingID] => 5
)
[1] => SimpleXMLElement Object
(
[settingName] => Home Page Stats
[settingDescription] => Show the Top 5 Teammate / Teamleader stats?
[requireValue] => 0
[localeID] => 14
[status] => 0
[value] => SimpleXMLElement Object
(
)
[settingID] => 3
)
)
)
I tried using xPath and have this so far:
$value = $fetchSettings->xpath("//settingName[text()='Test']/../value");
which returns:
Array ( [0] => SimpleXMLElement Object ( [0] => 66 ) )
How can I get the actual value and not just another array/object?
The end result will just be 66 for the example above.
SimpleXMLElement::xpath() returns a plain PHP array of "search results"; the first result will always be index 0 if any results were found.
Each "search result" is a SimpleXMLElement object, which has a magic __toString() method for getting the direct text content of a node (including CDATA, but including text inside child nodes, etc). The simplest way to call it is with (string)$my_element; (int)$my_element will also invoke it, then convert the result to an integer.
So:
$xpath_results = $fetchSettings->xpath("//settingName[text()='Test']/../value");
if ( count($xpath_results) > 0 ) {
$value = (string)$xpath_results[0];
}
Alternatively, the DOMXPath class can return results other than element and attribute nodes, due to the DOM's richer object model. For instance, you can have an XPath expression ending //text() to refer to the text content of a node, rather than the node itself (SimpleXML will do the search, but give you an element object anyway).
The downside is it's rather more verbose to use, but luckily you can mix and match the two sets of functions (using dom_import_simplexml() and its counterpart) as they have the same underlying representation:
// WARNING: Untested code. Please comment or edit if you find a bug!
$fetchSettings_dom = dom_import_simplexml($fetchSettings);
$xpath = new DOMXPath($fetchSettings_dom->ownerDocument);
$value = $xpath->evaluate(
"//settingName[text()='Test']/../value/text()",
$fetchSettings_dom
);
Because every element in a XML-file can appear as multiple times the parser always returns an array. If you are sure, that it is only a single item you can use current()
echo (string) current($value);
Note, that I cast the SimpleXMLElement to a string (see http://php.net/manual/simplexmlelement.tostring.php ) to get the actual value.
Use DomXPath class instead.
http://php.net/manual/en/domxpath.evaluate.php
The sample from php.net is just equivalent what you'd like to achieve:
<?php
$doc = new DOMDocument;
$doc->load('book.xml');
$xpath = new DOMXPath($doc);
$tbody = $doc->getElementsByTagName('tbody')->item(0);
// our query is relative to the tbody node
$query = 'count(row/entry[. = "en"])';
$entries = $xpath->evaluate($query, $tbody);
echo "There are $entries english books\n";
In this way, you can get values straight from the XML.
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.)
I'm just trying to figure out how to cleanly and nicely transform a XML collection into an appropriate object. See, I've got this very simple XML string :
$x = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<apiKeys>
<apiKey application="app1">HfxaoMBJJ9pLe</apiKey>
<apiKey application="app2">HfxaoMBJJ9pLeClsSHsh</apiKey>
<apiKey application="app3">HfxaoMBJJ9pLeClsSHshTI9qX</apiKey>
</apiKeys>';
Which I transform using :
$O_xmlElement = simplexml_load_string ($x);
This is what I get :
SimpleXMLElement Object
(
[apiKey] => Array
(
[0] => HfxaoMBJJ9pLe
[1] => HfxaoMBJJ9pLeClsSHsh
[2] => HfxaoMBJJ9pLeClsSHshTI9qX
)
)
And I'd rather have (I expected !) something like :
SimpleXMLElement Object
(
[apiKey] => Array
(
['app1'] => HfxaoMBJJ9pLe
['app2'] => HfxaoMBJJ9pLeClsSHsh
['app3'] => HfxaoMBJJ9pLeClsSHshTI9qX
)
)
Thank you very much for your help people
SimpleXML won't do what you want automatically. You'll have to build the object yourself:
$O_xmlElement = simplexml_load_string($x);
$myObject = new stdClass();
foreach ($O_xmlElement->apiKey as $apiKey) {
$key = (string) $apiKey['application'];
$myObject->${key} = (string) $apiKey;
}
Refer to the basic usage example in the PHP manual for good examples of dealing with child elements and attributes.
When getting attributes from a SimpleXMLElement, remember that each attribute will be a SimpleXMLElement and not a string. You'll want to explicitly cast each attribute to string before using it as an array key or object property name.
Not sure you can do it exactly as you want it, but you can check out php.net's docs here:
http://www.php.net/manual/en/simplexmlelement.attributes.php
Basically the attributes can be found inside an object attached to each of the apiKey objects.
You can use the following code:
$xml = simplexml_load_string($x);
$newArray = array();
$count=0;
foreach($xml as $value){
$key= (string)($xml->apiKey[$count++]->attributes()->application);
$newArray[$key] = $value[0];
}
$newArray = array_map("trim", $newArray);
print_r($newArray);
This will generate the following output:
Array
(
[app1] => HfxaoMBJJ9pLe
[app2] => HfxaoMBJJ9pLeClsSHsh
[app3] => HfxaoMBJJ9pLeClsSHshTI9qX
)
Further to my question here, I'm actually wondering why I'm not getting strings added to my array with the following code.
I get some HTML from an external source with this:
$doc = new DOMDocument();
#$doc->loadHTML($html);
$xml = #simplexml_import_dom($doc); // just to make xpath more simple
$images = $xml->xpath('//img');
$sources = array();
Here is the images array:
Array
(
[0] => SimpleXMLElement Object
(
[#attributes] => Array
(
[alt] => techcrunch logo
[src] => http://s2.wp.com/wp-content/themes/vip/tctechcrunch/images/logos_small/techcrunch2.png?m=1265111136g
)
)
...
)
Then I added the sources to my array with:
foreach ($images as $i) {
array_push($sources, $i['src']);
}
But when I print the results:
echo "<pre>";
print_r($sources);
die();
I get this:
Array
(
[0] => SimpleXMLElement Object
(
[0] => http://www.domain.com/someimages.jpg
)
...
)
Why isn't $i['src'] treated as a string? Isn't the original [src] element noted where I print $images a string inside there?
To put it another way $images[0] is a SimpleXMLElement, I understand that. But why is the 'src' attribute of THAT object not being but into $sources as a string when I reference it as $i['src']?
Why isn't $i['src'] treated as a string?
Becaue it isn't one - it's a SimpleXMLElement object that gets cast to a string if used in a string context, but it still remains a SimpleXMLElement at heart.
To make it a real string, force cast it:
array_push($sources, (string) $i['src']);
Because SimpleXMLElement::xpath() (quoting) :
Returns an array of SimpleXMLElement
objects
and not an array of strings.
So, the items of your $images array are SimpleXMLElement objects, and not strings -- which is why you have to cast them to strings, if you want strings.
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 ) ...
)
I did a foreach of the array like:
foreach ($ArrayName as $FieldLabel => $FieldValue) {
$Variable = $FieldValue[0]....
}
...but still, it gets the whole XML object as the field value. I wanted it to extract the value only not the whole object.
All simple xml objects are iteratable. Basically think of any object as a set, and some set's just happen to contain one object.
To extract your value do this
foreach($title as $item)
{
$list_of_titles = (string) $item;
}
print_r($list_of_titles);
So basically I typecast every item into a string from an object.