I have a small XML file:
<wddxPacket version='1.0'>
<header/>
<data>
<struct type='coldfusion.runtime.ArgumentCollection'>
<var name='HEADLINE'>
<string>Richard Barret's Articles on Leadership and High Performance Organisations</string>
</var>
</struct>
</data>
</wddxPacket>
I'm trying to use PHP SimpleXML and xpath to extract the value between the string element in the var name HEADLINE element. This code works:
// Location of the XML file on the file system
$file = 'http://10.10.200.37/skins/importscript/41802.xml';
$xml = simplexml_load_file($file);
// CREATE THE ARRAYS FOR EACH XML ELEMENT NEEDED
$title = $xml->xpath('//var[#name="HEADLINE"]');
echo "<p>";
print_r($title);
echo "</p>";
The problem is that it returns not only the value but also all the array information. As in:
Array (
[0] => SimpleXMLElement Object (
[#attributes] => Array (
[name] => HEADLINE
)
[string] => Richard Barret's Articles on Leadership and High Performance Organisations
)
)
How can I get it to return just the value and nothing else?
If I replace print_r with echo $title; I get the word Array on the page instead of the value. If I try echo $title[0]; I get nothing at all.
I've tried so many things now can't think of anything else! What am I doing wrong? Could anyone point me in the right direction? Thanks!
Sorry please ignore this!
Just after I posted the question I realised what I was doing wrong!
For anyone being as slow as I am today here was the problem...
$title = $xml->xpath('//var[#name="HEADLINE"]');
Should be:
$title = $xml->xpath('//var[#name="HEADLINE"]/string');
Now it works as it should.
You are interested in the nodeValue.
Example:
$xpath->evaluate("some_tag_name", $some_dom_element)->item(0)->nodeValue;
or use the string selector:
$title = $xml->xpath('//var[#name="HEADLINE"]/string');
As a habit I always add the 'string' type when getting values from SimpleXML
$title = (string) $xml->xpath('//var[#name="HEADLINE"]/string');
Not sure if this is good practice..
Related
I have a XML file named conf.xml and I'm trying to display the contents of this XML file via a simple php script (placed in the same directory) as follows:
conf.xml
<?xml version="1.0" encoding="UTF-8"?>
<registration_info>
<organization name="Home" />
</registration_info>
PHP script:
$data=simplexml_load_file("conf.xml");
$node=$data->registration_info;
$subnode=$node->organization;
echo (string) $subnode['name']; // Displays null string
I feel that there's nothing wrong with the code but the output is unexpected as the anticipated output was "Home". Can anyone please help me solve this problem and explain me the solution?
Thanks in advance.
The existing answer given here is correct, but the explanations are rather confused. SimpleXML is not hiding the root node of your XML, it's just that the object you have already is that node.
Each SimpleXMLElement object represents a particular node in the XML document's tree. There is no separate object in SimpleXML representing "the whole document", so when you run simplexml_load_file, the object returned is the SimpleXMLElement for the root node.
$root_node = simplexml_load_file("conf.xml");
echo $root_node->getName(); // registration_info
$child_node = $root_node->organization;
// Short for $root_node->organization[0];
// meaning "get the first child with name 'organization'
echo $child_node->getName(); // organization
Try this hope this will help you out. You have to just remove $node=$data->registration_info;
Try this snippet here
<?php
ini_set('display_errors', 1);
$data=simplexml_load_file("conf.xml");
$subnode=$data->organization;
echo (string) $subnode['name'];
Output: print_r($data)
SimpleXMLElement Object
(
[organization] => SimpleXMLElement Object
(
[#attributes] => Array
(
[name] => Home
)
)
)
I've got a problem when I want to convert an XML string into a SimpleXMLElement object.
This is my PHP code:
// XML string
$xmlStr = <<<XML
<?xml version='1.0'?>
<document>
<lastname lang="EN">Smith</lastname>
</document>
XML;
// Convert the XML string into an Array
$xml = simplexml_load_string($xmlStr);
$json = json_encode($xml);
$xmlArray = json_decode($json, true);
// Print SimpleXMLElement object
print_r($xml);
echo '<br /><br />';
// Print JSON
print_r($json);
echo '<br /><br />';
// Print XML Array
print_r($xmlArray);
echo '<br /><br />';
And I get this result:
SimpleXMLElement Object ( [lastname] => Smith )
{"lastname":"Smith"}
Array ( [lastname] => Smith )
But there is no "lang" attribute and I don't know what I'm doing wrong... :(
Anyone can help me, please? Any idea would be very appreciated! Thanks in advance.
To set the text value 'Smith' of the 'lastname' tag I do:
$xml->lastname->{'_'}
Hope it helps!
The json_encode function will not add xml attributes on any the node that is only text. If a final element is just text and you need to use another attribute as lang in this case, you'll have to add another enclosing mark. I always use _value, the same as do Mootools XmlToJsObject library.
I wrote a small PHP test .
I'm exploring XML and PHP, mostly XPath and other parsers.
Here be the xml:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:foo="http://www.foo.org/" xmlns:bar="http://www.bar.org">
<actors>
<actor id="1">Christian Bale</actor>
<actor id="2">Liam Neeson</actor>
<actor id="3">Michael Caine</actor>
</actors>
<foo:singers>
<foo:singer id="4">Tom Waits</foo:singer>
<foo:singer id="5">B.B. King</foo:singer>
<foo:singer id="6">Ray Charles</foo:singer>
</foo:singers>
<items>
<item id="7">Pizza</item>
<item id="8">Cheese</item>
<item id="9">Cane</item>
</items>
</root>
Here be my path & code:
$xml = simplexml_load_file('xpath.xml');
$result = $xml -> xpath('/root/actors');
echo '<pre>'.print_r($result,1).'</pre>';
Now, said path returns:
Array
(
[0] => SimpleXMLElement Object
(
[actor] => Array
(
[0] => Christian Bale
[1] => Liam Neeson
[2] => Michael Caine
)
)
)
Whereas a seemingly similar line of code, which I would have though would result in the singers, doesnt. Meaning:
$result = $xml -> xpath('/root/foo:singers');
Results in:
Array
(
[0] => SimpleXMLElement Object
(
)
)
Now I would've thought the foo: namespace in this case is a non-issue and both paths should result in the same sort of array of singers/actors respectively? How come that is not the case?
Thank-you!
Note: As you can probably gather I'm quite new to xml so please be gentle.
Edit: When I go /root/foo:singers/foo:singer I get results, but not before. Also with just /root I only get actors and items as results, foo:singers are completely omitted.
SimpleXML is, for a number of reasons, simply a bad API.
For most purposes I suggest PHP's DOM extension. (Or for very large documents a combination of it along with XMLReader.)
For using namespaces in xpath you'll want to register those you'd like to use, and the prefix you want to use them with, with your xpath processor.
Example:
$dom = new DOMDocument();
$dom->load('xpath.xml');
$xpath = new DOMXPath($dom);
// The prefix *can* match that used in the document, but it's not necessary.
$xpath->registerNamespace("ns", "http://www.foo.org/");
foreach ($xpath->query("/root/ns:singers") as $node) {
echo $dom->saveXML($node);
}
Output:
<foo:singers>
<foo:singer id="4">Tom Waits</foo:singer>
<foo:singer id="5">B.B. King</foo:singer>
<foo:singer id="6">Ray Charles</foo:singer>
</foo:singers>
DOMXPath::query returns a DOMNodeList containing matched nodes. You can work with it essentially the same way you would in any other language with a DOM implementation.
You can use // expression like:
$xml -> xpath( '//foo:singer' );
to select all foo:singer elements no matter where they are.
EDIT:
SimpleXMLElement is selected, you just can't see the child nodes with print_r(). Use SimpleXMLElement methods like SimpleXMLElement::children to access them.
// example 1
$result = $xml->xpath( '/root/foo:singers' );
foreach( $result as $value ) {
print_r( $value->children( 'foo', TRUE ) );
}
// example 2
print_r( $result[0]->children( 'foo', TRUE )->singer );
I have some xml, this is a simple version of it.
<xml>
<items>
<item abc="123">item one</item>
<item abc="456">item two</item>
</items>
</xml>
Using SimpleXML on the content,
$obj = simplexml_load_string( $xml );
I can use $obj->xpath( '//items/item' ); and get access to the #attributes.
I need an array result, so I have tried the json_decode(json_encode($obj),true) trick, but that looks to be removing access to the #attributes (ie. abc="123").
Is there another way of doing this, that provides access to the attributes and leaves me with an array?
You need to call attributes() function.
Sample code:
$xmlString = '<xml>
<items>
<item abc="123">item one</item>
<item abc="456">item two</item>
</items>
</xml>';
$xml = new SimpleXMLElement($xmlString);
foreach( $xml->items->item as $value){
$my_array[] = strval($value->attributes());
}
print_r($my_array);
Eval
You can go the route with json_encode and json_decode and you can add the stuff you're missing because that json_encode-ing follows some specific rules with SimpleXMLElement.
If you're interested into the rules and their details, I have written two blog-posts about it:
SimpleXML and JSON Encode in PHP – Part I
SimpleXML and JSON Encode in PHP – Part II
For you perhaps more interesing is the third part which shows how you can modify the json serialization and provide your own format (e.g. to preserve the attributes):
SimpleXML and JSON Encode in PHP – Part III and End
It ships with a full blown example, here is an excerpt in code:
$xml = '<xml>
<items>
<item abc="123">item one</item>
<item abc="456">item two</item>
</items>
</xml>';
$obj = simplexml_load_string($xml, 'JsonXMLElement');
echo $json = json_encode($obj, JSON_PRETTY_PRINT), "\n";
print_r(json_decode($json, TRUE));
Output of JSON and the array is as following, note that the attributes are part of it:
{
"items": {
"item": [
{
"#attributes": {
"abc": "123"
},
"#text": "item one"
},
{
"#attributes": {
"abc": "456"
},
"#text": "item two"
}
]
}
}
Array
(
[items] => Array
(
[item] => Array
(
[0] => Array
(
[#attributes] => Array
(
[abc] => 123
)
[#text] => item one
)
[1] => Array
(
[#attributes] => Array
(
[abc] => 456
)
[#text] => item two
)
)
)
)
$xml = new SimpleXMLElement($xmlString);
$xml is now an object. To get the value of an attribute:
$xml->something['id'];
Where 'id' is the name of the attribute.
While it's theoretically possible to write a generic conversion from XML to PHP or JSON structures, it is very hard to capture all the subtleties that might be present - the distinction between child elements and attributes, text content alongside attributes (as you have here) or even alongside child elements, multiple child nodes with the same name, whether order of child elements and text nodes is important (e.g. in XHTML or DocBook), etc, etc.
If you have a specific format you need to produce, it will generally be much easier to use an API - like SimpleXML - to loop over the XML and produce the structure you need.
You don't specify the structure you want to achieve, but the general approach given your input would be to loop over each item, and either access known attributes, or loop over each attribute:
$sxml = simplexml_load_string( $xml );
$final_array = array();
foreach ( $sxml->items->item as $xml_item )
{
$formatted_item = array();
// Text content of item
$formatted_item['content'] = (string)$xml_item;
// Specifically get 'abc' attribute
$formatted_item['abc'] = (string)$xml_item['abc'];
// Maybe one of the attributes is an integer
$formatted_item['foo_id'] = (int)$xml_item['foo_id'];
// Or maybe you want to loop over lots of possible attributes
foreach ( $xml_item->attributes() as $attr_name => $attr_value )
{
$formatted_item['attrib:' . $attr_name] = (string)$attr_value;
}
// Add it to a final list
$final_array[] = $formatted_item;
// Or maybe you want that array to be keyed on one of the attributes
$final_array[ (string)$xml_item['key'] ] = $formatted_item;
}
Here is a class I've found that is able to process XML into array very nicely: http://outlandish.com/blog/xml-to-json/ (backup). Converting to json is a matter of a json_encode() call.
I have an xml file
<?xml version="1.0" encoding="utf-8"?>
<xml>
<events date="01-10-2009" color="0x99CC00" selected="true">
<event>
<title>You can use HTML and CSS</title>
<description><![CDATA[This is the description ]]></description>
</event>
</events>
</xml>
I used xpath and and xquery for parsing the xml.
$xml_str = file_get_contents('xmlfile');
$xml = simplexml_load_string($xml_str);
if(!empty($xml))
{
$nodes = $xml->xpath('//xml/events');
}
i am getting the title properly, but iam not getting description.How i can get data inside
the cdata
SimpleXML has a bit of a problem with CDATA, so use:
$xml = simplexml_load_file('xmlfile', 'SimpleXMLElement', LIBXML_NOCDATA);
if(!empty($xml))
{
$nodes = $xml->xpath('//xml/events');
}
print_r( $nodes );
This will give you:
Array
(
[0] => SimpleXMLElement Object
(
[#attributes] => Array
(
[date] => 01-10-2009
[color] => 0x99CC00
[selected] => true
)
[event] => SimpleXMLElement Object
(
[title] => You can use HTML and CSS
[description] => This is the description
)
)
)
You are probably being misled into thinking that the CDATA is missing by using print_r or one of the other "normal" PHP debugging functions. These cannot see the full content of a SimpleXML object, as it is not a "real" PHP object.
If you run echo $nodes[0]->Description, you'll find your CDATA comes out fine. What's happening is that PHP knows that echo expects a string, so asks SimpleXML for one; SimpleXML responds with all the string content, including CDATA.
To get at the full string content reliably, simply tell PHP that what you want is a string using the (string) cast operator, e.g. $description = (string)$nodes[0]->Description.
To debug SimpleXML objects and not be fooled by quirks like this, use a dedicated debugging function such as one of these: https://github.com/IMSoP/simplexml_debug
This could also be another viable option, which would remove that code and make life a little easier.
$xml = str_replace("<![CDATA[", "", $xml);
$xml = str_replace("]]>", "", $xml);