I would like to loop through each book_list in the following xml file and for each book_list loop through each book for that book_list.
<inventory>
<book_list>
<book>
<author>Rowling</author>
<title>Harry Potter</title>
</book>
<book>
<author>Blyton</author>
<title>Famous 5</title>
</book>
</book_list>
<book_list>
<book>
<author>Bloggs</author>
<title>Learning XML</title>
</book>
<book>
<author>Jones</author>
<title>Beginning PHP</title>
</book>
</book_list>
</inventory>
How can I, for each book_list, loop through each book, using xpath in a php simplexml script? Here is my code,
$booklistpath = $xml->xpath('//booklist');
foreach ($booklistpath as $booklist) {
$bookpath = $xml->xpath('//book');
foreach ($bookpath as $book) {
...
}
}
The first loop is fine, it goes through each book_list - but the nested loop, which is meant to go through each book for that particular book_list goes through each book in the entire document. I have also tried :-
'.//book' and
'descendant::book'
That's the right result since you're using the second xpath call on the original $xml which is the SimpleXMLElement for your whole XML document.
To get the books for each booklist just iterate them as follow:
$booklists = $sxe->xpath('//book_list');
foreach ($booklists as $booklist) {
foreach ($booklist->book as $book) {
echo $book->asXML();
}
}
Related
I have an XML file to load into MySQL, but it is generated with a label and a value instead of being fully qualified.
So instead of
<book>
<name>Lord of the Flies</name>
<author>William Golding</author>
</book>
<book>
<name>War and Peace</name>
<author>Leo Tolstoy</author>
</book>
It is like
<book>
<label>name</label>
<value>Lord of the Flies</value>
<label>author</label>
<value>William Golding</value>
</book>
<book>
<label>name</label>
<value>War and Peace</value>
<label>author</label>
<value>Leo Tolstoy</value>
</book>
My code so far is -
book as $mybooks) {
$label=$mybooks->Label;
$value=$mybooks->Value;
echo "$label - $value";
}
}
?>
The output then is
Lord of the Flies - William Golding
War and Peace - Leo Tolstoy
Problem is that this does not have a reference to the field name which I can use to map it in my MySQL table.
What would be the best way to go about reading these into variables to push into a MySQL table (fields - name, author) using PHP?
Thanks
Steve
You can access the data using...
<?php
error_reporting ( E_ALL );
ini_set ( 'display_errors', 1 );
$xml = <<< XML
<books>
<book>
<label>name</label>
<value>Lord of the Flies</value>
<label>author</label>
<value>William Golding</value>
</book>
<book>
<label>name</label>
<value>War and Peace</value>
<label>author</label>
<value>Leo Tolstoy</value>
</book>
</books>
XML;
$doc = simplexml_load_string($xml);
foreach ( $doc->book as $book ) {
echo $book->label[0]."=".$book->value[0]."\n";
echo $book->label[1]."=".$book->value[1]."\n";
}
I've wrapped the book elements into a top level XML element to make it a valid document.
This is loaded using simplexml and then the foreach simply iterates over the books.
The labels and values will be an array in each element, so label[0] is for value[0]. You can use count($book->label) to work out how many elements there are.
The above code will output...
name=Lord of the Flies
author=William Golding
name=War and Peace
author=Leo Tolstoy
So, I'm parsing data from an XML feed in to php variables and everything is fine with the exception of the "link" element. It's not in a child like the others.
A cleaner, simpler example of the structure is below:
<bookstore>
<book category="children">
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
<link href="http://example.com">
</book>
<book category="web">
<title>Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
<link href="http://example.com">
</book>
</bookstore>
<bookstore>
<book category="children">
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
<link href="http://example.com">
</book>
<book category="web">
<title>Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
<link href="http://example.com">
</book>
</bookstore>
How do I read the link/href part of the XML in the parent node /bookstore/ and put it in to a string? It looks like it's been badly formatted, but I can't change it as it's supplied by a third party.
I thought I could load the entire /bookstore/ parent and search through it for the link and pull the value that way but it won't load the entire bookstore element.
My code is also extracting the other child tags fine and running through a loop to show the data in a list. Any help would be appreciated.
Edit: This is the link to the XML file I have to use: https://www.reddit.com/r/elderscrollsonline.xml
For SimpleXML - this code:
$rss = 'some_url_here';
$xml = simplexml_load_file($rss);
For you xml:
foreach($xml->bookstore as $bookstore) {
foreach ($bookastore as $book)
echo (string)$book->link['href'];
}
For links in https://www.reddit.com/r/elderscrollsonline.xml:
foreach($xml->entry as $book) echo (string)$book->link['href'];
I am having some issues using xmldiff package. I'm using xmldiff package 0.9.2; PHP 5.4.17; Apache 2.2.25.
For example I have two xml files: "from.xml" & "to.xml".
File "from.xml" contains:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<rott>
<NDC>321</NDC>
<NDC>123</NDC>
</rott>
</root>
File "to.xml" contains:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<rott>
<NDC>123</NDC>
<NDC>321</NDC>
</rott>
</root>
I'm using code:
$zxo = new XMLDiff\File;
$dir1 = dirname(__FILE__) . "/upload/from.xml";
$dir2 = dirname(__FILE__) . "/upload/to.xml";
$diff = $zxo->diff($dir1, $dir2);
$file = 'differences.xml';
file_put_contents($file, $diff);
I get result in "differences.xml" file:
<?xml version="1.0"?>
<dm:diff xmlns:dm="http://www.locus.cz/diffmark">
<root>
<rott>
<dm:delete>
<NDC/>
</dm:delete>
<dm:copy count="1"/>
<dm:insert>
<NDC>321</NDC>
</dm:insert>
</rott>
</root>
</dm:diff>
Could you please comment from where this:
<dm:delete>
<NDC/>
</dm:delete>
comes?
Also please kindly inform me if there is a method which differs two xml files without matter of xml nodes order?
What you see is the diff in the libdiffmark format. Right from that page:
<copy/> is used in places where the input subtrees are the same
The documents from your snippet have partially identical sub trees. Effectively the instructions libdiffmark will execute are
delete the whole subtree
copy 1 nodes, that means the node is the same in the both documents, so don't touch it
insert 1 new node
The order of the nodes matters. Please think about how a diff would look like, if the node order were ignored. Say you had 42 nodes and some of those were the same, how it would apply the copy instruction with the count? Much easier for a diff to use the exact node order of two documents. One interesting reading I've found here about why node order can be important.
Thanks.
If the document structure is known, I think you can simply sort the necessary parts. Here's a useful acticle about it. Based on it, I've poked on some examples and could sort a document by node values (just for example), please look here
document library.xml
<?xml version="1.0"?>
<library>
<book id="1003">
<title>Jquery MVC</title>
<author>Me</author>
<price>500</price>
</book>
<book id="1001">
<title>Php</title>
<author>Me</author>
<price>600</price>
</book>
<book id="1002">
<title>Where to use IFrame</title>
<author>Me</author>
<price>300</price>
</book>
<book id="1002">
<title>American dream</title>
<author>Hello</author>
<price>300</price>
</book>
</library>
The PHP code, sorting by the <title>
<?php
$dom = new DOMDocument();
$dom->load('library.xml');
$xp = new DOMXPath($dom);
$booklist = $xp->query('/library/book');
$books = iterator_to_array($booklist);
function sort_by_title_node($a, $b)
{
$x = $a->getElementsByTagName('title')->item(0);
$y = $b->getElementsByTagName('title')->item(0);
return strcmp($x->nodeValue, $y->nodeValue) > 0;
}
usort($books, 'sort_by_title_node');
$newdom = new DOMDocument("1.0");
$newdom->formatOutput = true;
$root = $newdom->createElement("library");
$newdom->appendChild($root);
foreach ($books as $b) {
$node = $newdom->importNode($b,true);
$root->appendChild($newdom->importNode($b,true));
}
echo $newdom->saveXML();
And here's the result:
<?xml version="1.0"?>
<library>
<book id="1002">
<title>American dream</title>
<author>Hello</author>
<price>300</price>
</book>
<book id="1003">
<title>Jquery MVC</title>
<author>Me</author>
<price>500</price>
</book>
<book id="1001">
<title>Php</title>
<author>Me</author>
<price>600</price>
</book>
<book id="1002">
<title>Where to use IFrame</title>
<author>Me</author>
<price>300</price>
</book>
</library>
This way you can sort the parts of the document before comparing. After that you can even use the DOM comparison directly. Even you could reorder the nodes, it were a similar approach.
I'm not sure it'll be very useful in the case if you have a variable node number. Say if the <NDC> tag were repeated some random number of times and it's values were completely different.
And after all, I still think the simplest way were to ask your supplicant to create some more predictable document structure :)
Thanks
Anatol
Hoi,
for example, i have a xml file :
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="WEB">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
</bookstore>
And I want all the titles and prices
I could write this code
<php
$res = $xpath->query('/bookstore/book/title');
foreach ($res as $item) {
echo "{$item->nodeValue}";
}
$res = $xpath->query('/bookstore/book/price');
foreach ($res as $item) {
echo "{$item->nodeValue}";
}
?>
But this looks very ugly,
Is there another possibility so that I can combine those 2 blocks of code?
something like this?
<php
$res = $xpath->query('/bookstore/book');
foreach ($res as $item) {
echo "{$item->title} <br/> {$item->price}";
}
?>
Kind regards
You could adjust your XPATH with concatenate to read '/bookstore/book/title | /bookstore/book/price'
This would give you a node list as such:
<title lang="en">Everyday Italian</title>
<price>30.00</price>
<title lang="en">XQuery Kick Start</title>
<price>49.99</price>
...
...
Then just pull values 1 and 2 in each foreach loop cycle for what you like.
Hope this helps!
Example of the xml:
<books>
<book>
<title>Hip Hop Hippo</title>
<released>31-12-9999</released>
</book>
<book>
<title>Bee In A Jar</title>
<released>01-01-0001</released>
</book>
</books>
I want to make a function that return the released date of a book title.
Ex: I want to get released date of the 'Hip Hop Hippo' book.
I know I can use simplexml and write ->book[0]->released. But that's only works when I have a static XML and I know where the ->book[$i]->title that match 'Hip Hop Hippo'. But not in dynamic case. I can't predict every changes, since it came from an API provider. It can be book[1], book[2], and so on.
What should I write in my function?
Check out the xpath functions http://php.net/manual/en/simplexmlelement.xpath.php
You will then be able to write a query like: /books/book[title="Hip Hop Hippo"]
$string = <<<XML
<books>
<book>
<title>Hip Hop Hippo</title>
<released>31-12-9999</released>
</book>
<book>
<title>Hip Hop Hippo</title>
<released>31-12-2000</released>
</book>
<book>
<title>Bee In A Jar</title>
<released>01-01-0001</released>
</book>
</books>
XML;
$xml = new SimpleXMLElement($string);
$result = $xml->xpath('/books/book[title="Hip Hop Hippo"]');
foreach($result as $key=>$node)
{
echo '<li>';
echo $node->title . ' / ' . $node->released;
echo '</li>';
}