I have a xml file output.xml
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<key type="your_id">CYBEX-525A/DA-IPOD</key>
<key type="web">cybex-525at-arc-trainer</key>
<key type="web">standard-console-2573</key>
<key type="name">Cybex 525AT Arc Trainer</key>
<key type="name">Standard console</key>
<review>
<order_id>1544346 1</order_id>
<author_nick>Brock</author_nick>
<author_email>bb#GMAIL.COM</author_email>
<date type="accepted">2013-10-14</date>
<comment type="overall">This cardio machine is exceptional. It works every part of your leg muscles if you rotate through the height settings and include calf-raises and squats during your routine. It also works your shoulders and biceps if you focus on working them while operating the arm poles. Unlike a standard elliptical it will raise your heart rate and cause you to sweat heavily soon after you start your routine. If you're a runner and are used to using a treadmill, you will feel satisfied after using this machine. It is kind of addictive because your body feels so good during and after use. I have combined 30 minutes on the treadmill with 30 minutes on the Arc for weight-loss, muscle tone, and cardiovascular training.</comment>
<score type="overall">5</score>
</review>
</item>
</items>
I need to save it in db, I'm just using below code to save data
if(file_exists('output.xml')){
$languages = simplexml_load_file("output.xml");
//echo '<pre>'; print_r($languages) ; die;
foreach($languages as $item){
echo '<pre>'; print_r($item->key) ; die;
foreach($item->review as $review){
$order_id = $review[0]->order_id;
$authorName = $review[0]->author_nick;
$authorEmail = strtolower($review[0]->author_email);
$comment = $review[0]->comment;
$score = $review[0]->score;
$date = $review[0]->date;
}
}
}
I need to get value of <key type="your_id">CYBEX-525A/DA-IPOD</key> and <key type="web">cybex-525at-arc-trainer</key> but unable to get data
when i print echo '<pre>'; print_r($item->key) ; die; within loop I'm getting following out put:
SimpleXMLElement Object
(
[#attributes] => Array
(
[type] => your_id
)
[0] => CYBEX-525A/DA-IPOD
[1] => cybex-525at-arc-trainer
[2] => standard-console-2573
[3] => Cybex 525AT Arc Trainer
[4] => Standard console
)
Is there any method for get all these data.
DOMXpath::evaluate() allows you to use expression to fetch nodes and scalar values from an XML DOM. The expression defines if the result is a node list or a scalar value. You can iterate a node list with foreach().
$document = new DOMDocument();
$document->loadXml($xmlString);
$xpath = new DOMXPath($document);
foreach ($xpath->evaluate('/items/item') as $item) {
var_dump(
[
'id' => $xpath->evaluate('string(key[#type="your_id"])', $item)
]
);
foreach ($xpath->evaluate('review', $item) as $review) {
var_dump(
[
'nick' => $xpath->evaluate('string(author_nick)', $review),
'email' => $xpath->evaluate('string(author_email)', $review)
]
);
}
}
Output:
array(1) {
["id"]=>
string(18) "CYBEX-525A/DA-IPOD"
}
array(2) {
["nick"]=>
string(5) "Brock"
["email"]=>
string(12) "bb#GMAIL.COM"
}
The second argument or DOMXpath::evaluate() is a context for the expression. So it is easy to iterate a list of nodes and fetch data for them.
Xpath functions like string() case the first node of a list into a scalar value. If the list was empty, and empty value of the type will be returned.
SimpleXML allows you fetch arrays of nodes using SimpleXMLElement::xpath(). Here is no direct way to fetch scalar values, but the implemented magic methods allow a compact syntax.
You will have to cast the returned SimpleXMLElement objects into strings.
$items = new SimpleXMLElement($xmlString);
foreach ($items->xpath('item') as $item) {
var_dump(
[
'id' => (string)$item->xpath('key[#type="your_id"]')[0]
]
);
foreach ($item->xpath('review') as $review) {
var_dump(
[
'nick' => (string)$review->author_nick,
'email' => (string)$review->author_email
]
);
}
}
If you won't use DOMDocument, you can try :
foreach($languages->item as $item){ //loop through item(s)
foreach($item->key as $key) { //loop through key(s)
if ($key["type"] == "your_id") echo $key; //echo CYBEX-525A/DA-IPOD
}
print_r($item->review); //prints review data
}
You can use as example
$xml="".$rs_rssfeed['rss_url']."";
$xmlDoc = new DOMDocument();
$xmlDoc->load($xml);
$xpath = new DOMXPath($xmlDoc);
$count = $xpath->evaluate('count(//item)');
if($count>0)
{
$x=$xmlDoc->getElementsByTagName('item');
for ($i=0; $i<$count = $xpath->evaluate('count(//item)'); $i++)
{
$item_title=$x->item($i)->getElementsByTagName('title')->item(0)->nodeValue;
$item_link=$x->item($i)->getElementsByTagName('link')->item(0)->nodeValue;
$item_desc=$x->item($i)->getElementsByTagName('description')->item(0)->nodeValue;
$item_pubdate=$x->item($i)->getElementsByTagName('pubDate')->item(0)->nodeValue;
}
}
Related
I can read an xml input in my code but the output has a SimpleXMLElement property. when I use echo the results has a "SimpleXMLElement ".
how can I delete this term from the out put?
SimpleXMLElement {#253 ▼
+0: "362932"
}
the code is
$xml=simplexml_load_file("$address&league=3");
// dd($xml);
$items = array();
foreach ($xml->Odds as $key => $value)
{
$matchid= ($value->FixtureMatch_Id);
echo "$matchid";
the xml is something like http://www.xmlsoccer.com/FootballDataDemo.asmx/GetFixtureMatchByID?ApiKey=ZXRIQOWMCFARAWRQIMSLRXCTSZDOBNLOTYWXYXMZYGDSENFSRB&Id=362933.
I want to eliminate SimpleXMLElement from the output.
You simply need to cast it into a string. SimpleXMLElement implements several interfaces and magic methods. For example Traversable to allow for foreach() and __toString() for string casting.
An echo is an implicit string cast, so it will output only the text content.
Here is a small example with a stripped down XML:
$xml = <<<'XML'
<?xml version="1.0" encoding="utf-8"?>
<XMLSOCCER.COM>
<Match>
<Id>362933</Id>
</Match>
</XMLSOCCER.COM>
XML;
$soccer = new SimpleXMLElement($xml);
foreach ($soccer->Match as $match) {
$matchId = $match->Id;
var_dump($matchId);
var_dump((string)$matchId);
echo $matchId;
}
Output:
object(SimpleXMLElement)#5 (1) {
[0]=>
string(6) "362933"
}
string(6) "362933"
362933
As you can see the property is a SimpleXMLElement (first var_dump()). But it can be cast into a string (second var_dump()) and an echo triggers an implicit cast.
How can I convert XML to an array that I can iterate over.
Here is the example of my XML
<user>
<student>yes</student>
<id>1</id>
<name>John</name>
</user>
<user>
<student>yes</student>
<id>1</id>
<name>Billy</name>
</user>
My php looks like this
$tmpTemplates = new XDomDocument();
$tmpTemplates->load('.....myFile.xml');
$xPath = new DomXPath($tmpTemplates);
$query = "//user[student='yes']";
$tmpTemplate = $xPath->query($query);
What I want to be able to do is
foreach($tmpTemplate as $tt){
var_dump($tt->student);
var_dump($tt->id);
var_dump($tt->name);
}
Now I'm only able to print out nodeValue which gives me something like this:
yes
1
John
How can I make it an array or an object so I can apprach each value by its key?
You used the same id for both records in your example, so I changed the id of the first one and added a document element.
<users>
<user>
<student>yes</student>
<id>2</id>
<name>John</name>
</user>
<user>
<student>yes</student>
<id>1</id>
<name>Billy</name>
</user>
</users>
Use DOMXpath:evaluate() to fetch the details, the second argument is the context node for the expression.
$document = new DOMDocument();
$document->loadXml($xmlString);
$xPath = new DomXPath($document);
$students = [];
foreach ($xPath->evaluate("//user[student='yes']") as $student) {
$id = $xPath->evaluate('string(id)', $student);
$students[$id] = [
'id' => $id,
'name' => $xPath->evaluate('string(name)', $student)
];
}
var_dump($students);
Output:
array(2) {
[2]=>
array(2) {
["id"]=>
string(1) "2"
["name"]=>
string(4) "John"
}
[1]=>
array(2) {
["id"]=>
string(1) "1"
["name"]=>
string(5) "Billy"
}
}
The return value depends on the expression. A location path like //user[student='yes'] or name returns a DOMNodeList. But you can cast the node list directly in Xpath. string(name) will return the contents of the first name child node or an empty string.
How can I make it an array or an object so I can apprach each value by its key?
Just for clarification, you've got an object so far, it's the SimpleXMLElement and you're using then $tt variable to access it:
...
var_dump($tt->student);
var_dump($tt->id);
var_dump($tt->name);
...
Now that $tt variable comes from another variable, namely by iterating over it, and the other variable is named $tmpTemplate:
...
foreach ($tmpTemplate as $tt) {
var_dump($tt->student);
...
That variable by the way is an array. So you can use it by using the index (starting at zero) to access each <user> element containing a <student> child-element with the value "student" in document-order (as you formulated the xpath for it):
$tmpTemplate[0] contains the first user SimpleXMLElement.
$tmpTemplate[1] contains the second user SimpleXMLElement.
... and so on and so forth.
I hope this makes this a bit more visible.
Try with this php function
xml_parse_into_struct
sample from php manual
<?php
$simple = "<para><note>simple note</note></para>";
$p = xml_parser_create();
xml_parse_into_struct($p, $simple, $vals, $index);
xml_parser_free($p);
echo "Index array\n";
print_r($index);
echo "\nVals array\n";
print_r($vals);
?>
You can use simplexml_load_file, which convert xml to object.
For instance :
$users = simplexml_load_file("users.xml");
foreach($users as $user) {
...
}
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.
-
Hello Everyone,
I'm trying to access data in a XML file:
<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://dublincore.org/documents/dcmi- namespace/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd";>
<responseDate>2013-04-15T12:14:31Z</responseDate>
<ListRecords>
<record>
<header>
<identifier>
a1b31ab2-9efe-11df-9922-efbb156aa6c1:01442b82-59a4-627e-800f-c63de74fc109
</identifier>
<datestamp>2012-08-16T14:42:52Z</datestamp>
</header>
<metadata>
<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd";>
<dc:description>...</dc:description>
<dc:date>1921</dc:date>
<dc:identifier>K11510</dc:identifier>
<dc:source>Waterschap Vallei & Eem</dc:source>
<dc:source>...</dc:source>
<dc:source>610</dc:source>
<dc:coverage>Bunschoten</dc:coverage>
<dc:coverage>Veendijk</dc:coverage>
<dc:coverage>Spakenburg</dc:coverage>
</oai_dc:dc>
</metadata>
<about>...</about>
</record>
This a a example of the XML.
I need to access data like dc:date dc:source etc.
Anyone any ideas?
Best regards,
Tim
-- UPDATE --
I'm now trying this:
foreach( $xml->ListRecords as $records )
{
foreach( $records AS $record )
{
$data = $record->children( 'http://www.openarchives.org/OAI/2.0/oai_dc/' );
$rows = $data->children( 'http://purl.org/dc/elements/1.1/' );
echo $rows->date;
break;
}
break;
}
You have nested elements that are in different XML namespaces. In concrete you have got two additional namespaces involved:
$nsUriOaiDc = 'http://www.openarchives.org/OAI/2.0/oai_dc/';
$nsUriDc = 'http://purl.org/dc/elements/1.1/';
The first one is for the <oai_dc:dc> element which contains the second ones * <dc:*>* elements like <dc:description> and so on. Those are the elements you're looking for.
In your code you already have a good nose how this works:
$data = $record->children( 'http://www.openarchives.org/OAI/2.0/oai_dc/' );
$rows = $data->children( 'http://purl.org/dc/elements/1.1/' );
However there is a little mistake: the $data children are not children of $record but of $record->metadata.
You also do not need to nest two foreach into each other. The code example:
$nsUriOaiDc = 'http://www.openarchives.org/OAI/2.0/oai_dc/';
$nsUriDc = 'http://purl.org/dc/elements/1.1/';
$records = $xml->ListRecords->record;
foreach ($records as $record)
{
$data = $record->metadata->children($nsUriOaiDc);
$rows = $data->children($nsUriDc);
echo $rows->date;
break;
}
/** output: 1921 **/
If you are running into problems like these, you can make use of $record->asXML('php://output'); to show which element(s) you are currently traversing to.
I think this is what you're looking for. Hope it helps ;)
use DomDocument for this like access to dc:date
$STR='
<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://dublincore.org/documents/dcmi- namespace/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd";>
<responseDate>2013-04-15T12:14:31Z</responseDate>
<ListRecords>
<record>
<header> <identifier> a1b31ab2-9efe-11df-9922-efbb156aa6c1:01442b82-59a4-627e-800f-c63de74fc109 </identifier>
<datestamp>2012-08-16T14:42:52Z</datestamp>
</header>
<metadata>
<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd";>
<dc:description>...</dc:description>
<dc:date>1921</dc:date>
<dc:identifier>K11510</dc:identifier>
<dc:source>Waterschap Vallei & Eem</dc:source>
<dc:source>...</dc:source>
<dc:source>610</dc:source>
<dc:coverage>Bunschoten</dc:coverage>
<dc:coverage>Veendijk</dc:coverage>
<dc:coverage>Spakenburg</dc:coverage>
</oai_dc:dc>
</metadata>
<about>...</about>
</record>';
$dom= new DOMDocument;
$STR= str_replace("&", "&", $STR); // disguise &s going IN to loadXML()
// $dom->substituteEntities = true; // collapse &s going OUT to transformToXML()
$dom->recover = TRUE;
#$dom->loadHTML('<?xml encoding="UTF-8">' .$STR);
// dirty fix
foreach ($dom->childNodes as $item)
if ($item->nodeType == XML_PI_NODE)
$dom->removeChild($item); // remove hack
$dom->encoding = 'UTF-8'; // insert proper
print_r($doc->getElementsByTagName('dc')->item(0)->getElementsByTagName('date')->item(0)->textContent);
output:
1921
or access to dc:source
$source= $doc->getElementsByTagName('dc')->item(0)->getElementsByTagName('source');
foreach($source as $value){
echo $value->textContent."\n";
}
output:
Waterschap Vallei & Eem
...
610
or give you array
$array=array();
$source= $doc->getElementsByTagName('dc')->item(0)->getElementsByTagName("*");
foreach($source as $value){
$array[$value->localName][]=$value->textContent."\n";
}
print_r($array);
output:
Array
(
[description] => Array
(
[0] => ...
)
[date] => Array
(
[0] => 1921
)
[identifier] => Array
(
[0] => K11510
)
[source] => Array
(
[0] => Waterschap Vallei & Eem
[1] => ...
[2] => 610
)
[coverage] => Array
(
[0] => Bunschoten
[1] => Veendijk
[2] => Spakenburg
)
)
Using XPath makes dealing with namespaces more straightforward:
<?php
// load the XML into a DOM document
$doc = new DOMDocument;
$doc->load('oai-response.xml'); // or use $doc->loadXML($xml) for an XML string
// bind the DOM document to an XPath object
$xpath = new DOMXPath($doc);
// map all the XML namespaces to prefixes, for use in XPath queries
$xpath->registerNamespace('oai', 'http://www.openarchives.org/OAI/2.0/');
$xpath->registerNamespace('oai_dc', 'http://www.openarchives.org/OAI/2.0/oai_dc/');
$xpath->registerNamespace('dc', 'http://purl.org/dc/elements/1.1/');
// identify each record using an XPath query
// collect data as either strings or arrays of strings
foreach ($xpath->query('oai:ListRecords/oai:record/oai:metadata/oai_dc:dc') as $item) {
$data = array(
'date' => $xpath->evaluate('string(dc:date)', $item), // $item is the context for this query
'source' => array(),
);
foreach ($xpath->query('dc:source', $item) as $source) {
$data['source'][] = $source->textContent;
}
print_r($data);
}
Here is an example bit from the xml file:
<array>
<dict>
<key>Name</key>
<string>Joe Smith</string>
<key>Type</key>
<string>Profile</string>
<key>Role</key>
<string>User</string>
<key>Some Number</key>
<integer>1</integer>
<key>Some Boolean</key>
<true/>
</dict>
</array>
I have two separate goals. The first is to extract an array from the dictnode that would look like:
[Name] => Joe Smith
[Type] => Profile
[Role] => User
[Some Number] => 1
[Some Boolean] => true
It's not crucial that the boolean be included, so if that adds too much complexity, I'd rather just know how to deal with the others for now.
The second goal is to be able to select the value node (<string>, <integer>,etc) so that I can change the value. I know I would need to select it based on the text value of the preceding key element.
I think the following XPath should work:
//key[.=$keyname]/following-sibling[1]
But I'm not sure.
Basically, this whole system that Apple uses seems logical, but totally contrary to my understanding of how XML is supposed to work. If I ran the world, the original XML would look more like:
<dict type="array">
<value key="Name" type="string">Joe Smith</value>
<value key="Type" type="string">Profile</value>
<value key="Role type="string">User</value>
<value key="Some Number" type="integer">1</value>
<value key="Some Boolean" type="boolean">true</value>
</dict>
But since it is fairly logical, I am wondering if I'm missing some obvious way of handling it.
That xpath selector looks okay for that purpose to me. You'd probably want to check the value of the sibling you retrieve, to see if it's the tag you want to be changing (in the boolean case) or indeed the element's value.
In terms of extracting the data into an array, something along the lines of ...
$xml = new SimpleXMLElement($data);
$output = array();
$key = null;
$value = null;
foreach ($xml->dict->children() as $child) {
if ($child->getName() == 'key') {
$key = (string)$child;
} elseif (!is_null($key)) {
$childStr = (string)$child;
$value = (empty($childStr) ? $child->getName() : $childStr);
}
if (!is_null($key) && !is_null($value)) {
$output[$key] = $value;
$key = null;
$value = null;
}
}
var_dump($output);
... should do what you're looking for from what I understand.
James
You can use
/plist/dict/key[.='Name']/following-sibling::*[1]
to select the string-node "Joe Smith" and update it accordingly.
My PHP code for updating XML files using XPath looks like this:
function updateNodesByXPath($xmlin, $expression, $value) {
$dom = new DOMDocument();
$dom->preserveWhiteSpace = true;
$dom->formatOutput = false;
$dom->loadXML($xmlin);
$xpath = new DOMXPath($dom);
$elements = $xpath->query($expression);
foreach ($elements as $element) {
$element->nodeValue = $value;
}
$xmlout = $dom->saveXML($dom, LIBXML_NOEMPTYTAG);
// Add XML header when present before (DOMDocument::saveXML will remove it)
if (strncasecmp($xmlin, '<?xml', 5)) {
$xmlout = preg_replace('~<\?xml[^>]*>\s*~sm', '', $xmlout);
}
return $xmlout;
}