parsing xml with php and xpath - getting paths right - php

I am trying to parse an XML file with php SimpleXML and xpath and having problems getting the paths right and accessing attribute values. Any help appreciated.
Here is my xml file:-
<message:MessageGroup xmlns="http://mynamespace.com">
<Book>
<BookKey>
<Value concept="TITLE" value="Gone Girl"/>
<Value concept="AUTHOR" value="Gillian Flynn"/>
</BookKey>
<Sales>
<Month>Jan</Month>
<Number value="20"/>
</Sales>
<Sales>
<Month>Feb</Month>
<Number value="15"/>
</Sales>
<Sales>
<Month>Mar</Month>
<Number value="30"/>
</Sales>
</Book>
<Book>
<BookKey>
<Value concept="TITLE" value="Inferno"/>
<Value concept="AUTHOR" value="Dan Brown"/>
</BookKey>
<Sales>
<Month>Jan</Month>
<Number value="10"/>
</Sales>
<Sales>
<Month>Feb</Month>
<Number value="15"/>
</Sales>
<Sales>
<Month>Mar</Month>
<Number value="3"/>
</Sales>
</Book>
</message:MessageGroup>
I need to parse this to get the following output:-
Gone Girl,Gillian Flynn,Jan,20
Gone Girl,Gillian Flynn,Feb,15
Gone Girl,Gillian Flynn,Mar30
Inferno,Dan Brown,Jan,10
Inferno,Dan Brown,Feb,15
Inferno,Dan Brown,Mar,3
I have written the following code:-
<?php
$xmlfile = 'Books.xml';
$xml = simplexml_load_file($xmlfile);
$namespace = 'http://mynamespace.com';
$xml->registerXPathNamespace('ns',$namespace);
$books = $xml->xpath('//ns:Book');
//loop through each book -
foreach($books as $book) {
$values = $xml->xpath('ns:BookKey/Value');
$bookkeys= array();
//loop through each Book's BookKey Values and push them to array -
foreach($values as $value){
array_push($bookkeys, $value->attributes()->value);
}
$sales = $xml->xpath('ns:Sales');
//loop through each Book's Sales and write out Month and Number value after the book key values -
foreach($sales as $sale){
foreach($bookkeys as $bk){
echo $bk.",";
}
echo $sale->Month;
echo ",";
echo $sale->Number->attributes()->value;
echo "\n";
}
}
?>
From a var_dump, the $books array appears to be ok; $values and $sales are empty arrays though, so clearly the paths are not right. I have tried leaving out the namespace but still get an empty array; I want to avoid pulling out all the instances of values and sales in the file as opposed to just the ones for each particular book.

You are missing the namespace definition for the prefix message.
To get the value from Value, message:MessageGroup/ns:BookKey/ns:Value/#value will be the XPath to use.

Related

Reading data from xml with php

please can anybody help me on how to retrieve data from the xml document below
<?xml version="1.0" encoding="utf-8"?>
<livescore-feed timestamp-created="1279745722">
<item id="afab6479053955afcd07dbe2eb972feb" status= "finished" timestamp-starts= "1278255600">
<teams>
<hosts id="zenspbfc_rus">
<name>Zenit</name>
<fullname>FC Zenit St. Petersburg</fullname>
</hosts>
<guests id="anzmfc_rus">
<name>Anzhi</name>
<fullname>FC Anzhi Makhachkala </fullname>
</guests>
</teams>
<events>
<event type="goal" team="hosts">
<player>Bystrov</player>
<minute>15</minute>
<score>1 - 0</score>
</event>
<event type="yellow_card" team="guests">
<player>Tsorayev</player>
<minute>40</minute>
</event>
</events>
</item>
</livescore-feed>
i have tried the following and i was able to some data, except from the events node, the events contains some nodes event. the problem i have is how to get the event from the events node
$xml = simplexml_load_string($data);
$match_id = $xml->{livescorefullname-feed}->matches->item[$i]['id'];
echo $match_id;
In SimpleXML, you don't specify the root node, because the SimpleXML object ($xml) is the root node.
This loops through each event and outputs the details.
foreach($xml->item->events->event as $event){
echo $event->score;
echo $event->player;
echo $event->attributes()->type;
echo $event->attributes()->team;
}

How to output all XML element attributes using a foreach loop?

New to XML here.
Consider this simple XML schema:
<products>
<category name="furniture">
<item name="chair" name2="table">
<size>
<small>10</small>
<large>20</large>
</size>
</item>
<item name="cabinet">
<size>
<small>15</small>
<large>30</large>
</size>
</item>
<item name="shelf" name2="box" name3="frame">
<size>
<small>5</small>
<large>10</large>
</size>
</item>
</category>
</products>
Notice each <item> element has a different amount of attributes.
I've been trying to echo out the attributes using XPATH and a foreach loop without success.
Surely I'm missing a small piece of syntax.
$dom=simplexml_load_file("file.xml");
foreach ($dom->xpath("/products/category[#name='furniture']/item") as $parse)
echo'<tr><td>'.$parse->attributes().'</td></tr>';
$parse->attributes(); only gives me the first attribute of the element.
Output looks like this:
<tr><td>chair</td></tr>
<tr><td>cabinet</td></tr>
<tr><td>shelf</td></tr>
What am I missing?
Use a nested foreach loop:
foreach ($dom->xpath("/products/category[#name='furniture']/item") as $parse) {
foreach ($parse->attributes() as $attr) {
echo '<tr><td>'. $attr . '</td></tr>'."\n";
}
}

Parse XML Parent node of matching attribute

I have an XML like the one below, I am trying to do an xpath query and parse it with simplexml. The XML is a CURL response and is stored in a $response variable. I need to look the Code attribute inside the <Item> and select the parent <Product> to parse it.
$response:
<Items>
<Product>
<Item Code="123">
</Item>
<Price>170
</Price>
</Product>
<Product>
<Item Code="456">
</Item>
<Price>150
</Price>
</Product>
</Items>
This is what I am doing:
$xml = simplexml_import_dom($response);
function loadNode($code){
global $xml;
$scode = $xml->xpath('//Item[contains(#Code,"' . $code . '")]/..');
echo $scode->Items->Product->Price;
}
loadNode("123");
This is the Notice I get:
Notice: Trying to get property of non-object
A couple of observations:
The xpath() method returns an array of SimpleXMLElement
objects, not a single SimpleXMLElement. (Yes, even though there can only be a single parent of an element, you still have to get it as the first member of the array ([0]).
$scode->Items->Product->Price should be changed to just
$scode->Price.
These modifications to your PHP code:
<?php
$response = <<<XML
<Items>
<Product>
<Item Code="123">
</Item>
<Price>170
</Price>
</Product>
<Product>
<Item Code="456">
</Item>
<Price>150
</Price>
</Product>
</Items>
XML;
$xml = simplexml_load_string($response);
function loadNode($code) {
global $xml;
$scode = $xml->xpath('//Item[contains(#Code,' . $code . ')]/..')[0];
echo $scode->Price;
}
loadNode("123");
?>
When run will yield this output:
170
as expected.

PHP SimpleXML add child to each parent repeatedly

I have this kind of XML:
<?xml version="1.0" encoding="utf-8"?>
<data>
<stats>
</stats>
<params>
</params>
<results>
<record id='SJDGH'>
<item>abc</item>
<item>def</item>
<item>ghi</item>
</record>
<record id='OIIO'>
<item>abc</item>
<item>def</item>
<item>ghi</item>
</record>
</results>
</data>
I'm generating a new <item> for every <record> in <results> in a loop:
// $data is SimpleXml objec from XML above
foreach ($data->results->record as $record)
{
$newitem = 'New item!'.time().$record->attributes()->id;
}
Somehow in this loop i need to change the SimpleXML object ($data) to contain new items in every <record>.
is it possible?
I needed a little guessing, but this might what you're looking for:
$records = $data->results->record;
foreach($records as $record)
{
$value = sprintf('New Item! %s / id:%s', time(), $record['id']);
$record->item[] = $value;
}
$data->asXML('php://output');
See it in action.
I think you might want to use addChild.
Check it out here: http://php.net/manual/en/simplexmlelement.addchild.php

Parse XML in PHP by specific attribute

I need to get <name> and <URL> tag's value where subtype="mytype".How can do it in PHP?
I want document name and test.pdf path in my result.
<?xml version="1.0" encoding="UTF-8"?>
<test>
<required>
<item type="binary">
<name>The name</name>
<url visibility="restricted">c:/temp/test/widget.exe</url>
</item>
<item type="document" subtype="mytype">
<name>document name</name>
<url visiblity="visible">c:/temp/test.pdf</url>
</item>
</required>
</test>
Use SimpleXML and XPath, eg
$xml = simplexml_load_file('path/to/file.xml');
$items = $xml->xpath('//item[#subtype="mytype"]');
foreach ($items as $item) {
$name = (string) $item->name;
$url = (string) $item->url;
}
PHP 5.1.2+ has an extension called SimpleXML enabled by default. It's very useful for parsing well-formed XML like your example above.
First, create a SimpleXMLElement instance, passing the XML to its constructor. SimpleXML will parse the XML for you. (This is where I feel the elegance of SimpleXML lies - SimpleXMLElement is the entire library's sole class.)
$xml = new SimpleXMLElement($yourXml);
Now, you can easily traverse the XML as if it were any PHP object. Attributes are accessible as array values. Since you're looking for tags with specific attribute values, we can write a simple loop to go through the XML:
<?php
$yourXml = <<<END
<?xml version="1.0" encoding="UTF-8"?>
<test>
<required>
<item type="binary">
<name>The name</name>
<url visibility="restricted">c:/temp/test/widget.exe</url>
</item>
<item type="document" subtype="mytype">
<name>document name</name>
<url visiblity="visible">c:/temp/test.pdf</url>
</item>
</required>
</test>
END;
// Create the SimpleXMLElement
$xml = new SimpleXMLElement($yourXml);
// Store an array of results, matching names to URLs.
$results = array();
// Loop through all of the tests
foreach ($xml->required[0]->item as $item) {
if ( ! isset($item['subtype']) || $item['subtype'] != 'mytype') {
// Skip this one.
continue;
}
// Cast, because all of the stuff in the SimpleXMLElement is a SimpleXMLElement.
$results[(string)$item->name] = (string)$item->url;
}
print_r($results);
Tested to be correct in codepad.
Hope this helps!
You can use the XML Parser or SimpleXML.

Categories