Parsing xml data in an order using xquery - php

I have a xml file like this
<?xml version="1.0" encoding="UTF-8"?>
<gallery >
<gallerydata>
<galleryname>
Home
</galleryname>
<createdat>
14/8/2010 4:53 pm
</createdat>
</gallerydata>
<gallerydata>
<galleryname>
School
</galleryname>
<createdat>
13/8/2010 5:19 pm
</createdat>
</gallerydata>
<gallerydata>
<galleryname>
Company
</galleryname>
<createdat>
15/8/2010 5:21 pm
</createdat>
</gallerydata>
</gallery>
Iam using xpath and xquery for parsing this xml file
$xml_str = file_get_contents($file);
$xml = simplexml_load_string($xml_str);
$nodes = $xml->xpath('//gallery/gallerydata');
How i can get the latest gallery data, i mean latest file (the data with last date )
Is their any way to do ?Please help me

IF you looking for a way to do it with the initial xpath query that could be rough, i dont think xpath 1.0 can parse dates so youd have to do some convoluted string comparison... If youre ok with doing it in php after the fact theres a multitude of ways... for example:
$xml = new SimpleXmlElement($xmlStr);
$gallerydata = $xml->xpath('//gallerydata');
$nodes = array();
foreach($gallerydata as $entry)
{
$utime = strtotime(str_replace('/', '-',$entry->createdat));
$nodes[$utime] = $entry;
}
krsort($nodes);
$newest = array_shift($nodes);
echo $newest->asXml(); // or do wht you need to do with the entry data...
This loops over your renturned node set and puts the nodes in an array indexed by unix time. Then I run a reversed key sort and shift the first element off the array. Note, that since im using strtotime for the conversion and you have UK date formats we have to do a string replace on the slashes.

Since your file does not store gallery data in chronological order, you can get the latest gallery entry only by looping through all the entries.

Related

PHP not returning full XML file contents [duplicate]

I'm trying to read an RSS feed from Flickr but it has some nodes which are not readable by Simple XML (media:thumbnail, flickr:profile, and so on).
How do I get round this? My head hurts when I look at the documentation for the DOM. So I'd like to avoid it as I don't want to learn.
I'm trying to get the thumbnail by the way.
The solution is explained in this nice article. You need the children() method for accessing XML elements which contain a namespace. This code snippet is quoted from the article:
$feed = simplexml_load_file('http://www.sitepoint.com/recent.rdf');
foreach ($feed->item as $item) {
$ns_dc = $item->children('http://purl.org/dc/elements/1.1/');
echo $ns_dc->date;
}
With the latest version, you can now reference colon nodes with curly brackets.
$item->{'itunes:duration'}
You're dealing with a namespace? I think you need to use the ->children method.
$ns_dc = $item->children('http://namespace.org/');
Can you provide a snippet with the xml declaration?
An even simpler method using PHP of accessing namespaced XML nodes without declaring a namespace is....
In order to get the value of <su:authorEmail> from the following source
<item>
<title>My important article</title>
<pubDate>Mon, 29 Feb 2017 00:00:00 +0000</pubDate>
<link>https://myxmlsource.com/32984</link>
<guid>https://myxmlsource.com/32984</guid>
<author>Blogs, Jo</author>
<su:departments>
<su:department>Human Affairs</su:department>
</su:departments>
<su:authorHash>4f329b923419b3cb2c654d615e22588c</su:authorHash>
<su:authorEmail>hIwW14tLc+4l/oo7agmRrcjwe531u+mO/3IG3xe5jMg=</su:authorEmail>
<dc:identifier>/32984/Download/0032984-11042.docx</dc:identifier>
<dc:format>Journal article</dc:format>
<dc:creator>Blogs, Jo</dc:creator>
<slash:comments>0</slash:comments>
</item>
Use the following code:
$rss = new DOMDocument();
$rss->load('https://myxmlsource.com/rss/xml');
$nodes = $rss->getElementsByTagName('item');
foreach ($nodes as $node) {
$title = $node->getElementsByTagName('title')->item(0)->nodeValue;
$author = $node->getElementsByTagName('author')->item(0)->nodeValue;
$authorHash = $node->getElementsByTagName('authorHash')->item(0)->nodeValue;
$department = $node->getElementsByTagName('department')->item(0)->nodeValue;
$email = decryptEmail($node->getElementsByTagName('authorEmail')->item(0)->nodeValue);
}

Check if child exists? - SimpleXML (PHP)

I have different XML files where I renamed for each XML file all individual tags, so that every XML file has the same tag name. That was easy because the function was customized for the XML file.
But instand of writing 7 new functions for each XML file now I want to check if a XML file has a specidifed child or not. Because if I want to say:
foreach ($items as $item) {
$node = dom_import_simplexml($item);
$title = $node->getElementsByTagName('title')->item(0)->textContent;
$price = $node->getElementsByTagName('price')->item(0)->textContent;
$url = $node->getElementsByTagName('url')->item(0)->textContent;
$publisher = $node->getElementsByTagName('publisher')->item(0)->textContent;
$category = $node->getElementsByTagName('category')->item(0)->textContent;
$platform = $node->getElementsByTagName('platform')->item(0)->textContent;
}
I get sometimes: PHP Notice: Trying to get property of non-object in ...
For example. Two different XML sheets. One contains publisher, category and platform, the other not:
XML 1:
<products>
<product>
<desc>This is a Test</desc>
<price>11.69</price>
<price_base>12.99</price_base>
<publisher>Stackoverflow</publisher>
<category>PHP</category>
</packshot>
<title>Check if child exists? - SimpleXML (PHP)</title>
<url>http://stackoverflow.com/questions/ask</url>
</product>
</products>
XML 2:
<products>
<product>
<image></image>
<title>Questions</title>
<price>23,90</price>
<url>google.de/url>
<platform>Stackoverflow</platform>
</product>
</products>
You see, sometimes one XML file contains publisher, category and platform but sometimes not. But it could also be that not every node of a XML file contains all attributes like in the first!
So I need to check for every node of a XML file individual if the node is containing publisher, category or/and platform.
How can I do that with SimpleXML?
I thought about switch case but at first I need to check which childs are contained in every node.
EDIT:
Maybe I found a solution. Is that a solution or not?
if($node->getElementsByTagName('platform')->item(0)){
echo $node->getElementsByTagName('platform')->item(0)->textContent . "\n";
}
Greetings and Thank You!
One way to rome... (working example)
$xml = "<products>
<product>
<desc>This is a Test</desc>
<price>11.69</price>
<price_base>12.99</price_base>
<publisher>Stackoverflow</publisher>
<category>PHP</category>
<title>Check if child exists? - SimpleXML (PHP)</title>
<url>http://stackoverflow.com/questions/ask</url>
</product>
</products>";
$xml = simplexml_load_string($xml);
#set fields to look for
foreach(['desc','title','price','publisher','category','platform','image','whatever'] as $path){
#get the first node
$result = $xml->xpath("product/{$path}[1]");
#validate and set
$coll[$path] = $result?(string)$result[0]:null;
#if you need here a local variable do (2 x $)
${$path} = $coll[$path];
}
#here i do array_filter() to remove all NULL entries
print_r(array_filter($coll));
#if local variables needed do
extract($coll);#this creates $desc, $price
Note </packshot> is an invalid node, removed here.
xpath syntax https://www.w3schools.com/xmL/xpath_syntax.asp
Firstly, you're over-complicating your code by switching from SimpleXML to DOM with dom_import_simplexml. The things you're doing with DOM can be done in much shorter code with SimpleXML.
Instead of this:
$node = dom_import_simplexml($item);
$title = $node->getElementsByTagName('title')->item(0)->textContent;
you can just use:
$title = (string)$item->title[0];
or even just:
$title = (string)$item->title;
To understand why this works, take a look at the SimpleXML examples in the manual.
Armed with that knowledge, you'll be amazed at how simple it is to see if a child exists or not:
if ( isset($item->title) ) {
$title = (string)$item->title;
} else {
echo "There is no title!";
}

Stripping some data from XML field using php

I've currently got an XML file that's being imported using a plugin on WordPress.
I've got a field that's currently importing the data and time automatically, but the field contains data in the current format
<EndDate>2014-04-24T00:00:00+01:00</EndDate>
I need to remove the time field somehow, but I'm stuck on how to do this (using php).
Is there a way of creating a function to remove the time, so that the field pulls in;
<EndDate>YYYY-MM-DD</EndDate>
Please help.
You first need to exactly locate that field. You could use simplexml.
To output a formatted date you use the date function. It accepts a UNIX timestamp.
You obtain that timestamp by using the strtotime function, that takes a date+time formatted as a string and returns a UNIX timestamp.
Here is one way you can do what you are looking at
$xml = "<reservation>
<detail>
<name>XXXXX</name>
<EndDate>2014-04-24T00:00:00+01:00</EndDate>
</detail>
<detail>
<name>YYYYY</name>
<EndDate>2014-04-24T00:00:00+01:00</EndDate>
</detail>
</reservation>";
$data = simplexml_load_string($xml);
//rint_r($data);
foreach ($data->detail as $obj)
{
$end_date = $obj->EndDate ;
$date = new DateTime($end_date);
$obj->EndDate = $date->format("Y-m-d");
}
$new_xml = $data->asXML();
echo $new_xml ;
You can also save as file
$data->asXML($file);
I have used for loading the string as
$data = simplexml_load_string($xml);
You can load the file also as
$data = simplexml_load_file($file);
Xpath can be used so fetch nodes from the XML DOM, but it can extract scalar values, too. It has even some functions. One of them is string-before() that returns the part of a string before an substring.
$xml = "<EndDate>2014-04-24T00:00:00+01:00</EndDate>";
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
var_dump(
$xpath->evaluate('substring-before(//EndDate, "T")')
);
Output: https://eval.in/141939
string(10) "2014-04-24"

Ebay api GetSellerList, Parsing response XML

I am using the ebay trading api to get a sellers stock which is currently listed. I am using the call GetSellerList.I am having trouble parsing the xml which I would then insert into there website shop.
This is the xml request.
<GetSellerListRequest xmlns='urn:ebay:apis:eBLBaseComponents'>
<UserID>".$user_id."</UserID>
<DetailLevel>ReturnAll</DetailLevel>
<ErrorLanguage>RFC 3066</ErrorLanguage>
<WarningLevel>Low</WarningLevel>
<Version>".$compat_level."</Version>
<RequesterCredentials>
<eBayAuthToken>".$auth_token."</eBayAuthToken>
</RequesterCredentials>
<StartTimeFrom>2012-06-12T23:35:27.000Z</StartTimeFrom>
<StartTimeTo>2012-08-30T23:35:27.000Z</StartTimeTo>
<Pagination>
<EntriesPerPage>200</EntriesPerPage>
</Pagination>
<OutputSelector>ItemArray.Item.Title</OutputSelector>
<OutputSelector>ItemArray.Item.Description</OutputSelector>
<OutputSelector>ItemArray.Item.BuyItNowPrice</OutputSelector>
<OutputSelector>ItemArray.Item.Quantity</OutputSelector>
</GetSellerListRequest>
I am not the best with php, I am still learning so i have looked through w3schools and php docs and found nothing. I have been using this (off of ebay tuts) to try and get the values of the xml tags by using getElementsByTagName.
$dom = new DOMDocument();
$dom->loadXML($response);
$titles = $dom->getElementsByTagName('Title')->length > 0 ? $dom->getElementsByTagName('Title')->item(0)->nodeValue : '';
Now i was hoping that i would be able to create an array with this then use foreach to insert them into the db but when i use this it only gets the value of the first 'Title' tag
Im sure there is a way to create an array with all values of 'Title' in it.
All help is appreciated.
This would be easier to answer if you posted the response XML (just the relevant portion) rather than the request.
The code you have will only grab the first item - specifically this part:
$dom->getElementsByTagName('Title')->item(0)->nodeValue
Rather, you'll want to loop through all the Title elements and extract their nodeValue. This is a starting point:
$dom = new DOMDocument();
$dom->loadXML($response);
$title_nodes = $dom->getElementsByTagName('Title');
$titles = array();
foreach ($title_nodes as $node) {
$titles[] = $node->nodeValue;
}

Using DOMXml and Xpath, to update XML entries

Hello I know there is many questions here about those three topics combined together to update XML entries, but it seems everyone is very specific to a given problem.
I have been spending some time trying to understand XPath and its way, but I still can't get what I need to do.
Here we go
I have this XML file
<?xml version="1.0" encoding="UTF-8"?>
<storagehouse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="schema.xsd">
<item id="c7278e33ef0f4aff88da10dfeeaaae7a">
<name>HDMI Cable 3m</name>
<weight>0.5</weight>
<category>Cables</category>
<location>B3</location>
</item>
<item id="df799fb47bc1e13f3e1c8b04ebd16a96">
<name>Dell U2410</name>
<weight>2.5</weight>
<category>Monitors</category>
<location>C2</location>
</item>
</storagehouse>
What I would like to do is to update/edit any of the nodes above when I need to. I will do a Html form for that.
But my biggest conserne is how do I find and update a the desired node and update it?
Here I have some of what I am trying to do
<?php
function fnDOMEditElementCond()
{
$dom = new DOMDocument();
$dom->load('storage.xml');
$library = $dom->documentElement;
$xpath = new DOMXPath($dom);
// I kind of understand this one here
$result = $xpath->query('/storagehouse/item[1]/name');
//This one not so much
$result->item(0)->nodeValue .= ' Series';
// This will remove the CDATA property of the element.
//To retain it, delete this element (see delete eg) & recreate it with CDATA (see create xml eg).
//2nd Way
//$result = $xpath->query('/library/book[author="J.R.R.Tolkein"]');
// $result->item(0)->getElementsByTagName('title')->item(0)->nodeValue .= ' Series';
header("Content-type: text/xml");
echo $dom->saveXML();
}
?>
Could someone maybe give me an examples with attributes and so on, so one a user decides to update a desired node, I could find that node with XPath and then update it?
The following example is making use of simplexml which is a close friend of DOMDocument. The xpath shown is the same regardless which method you use, and I use simplexml here to keep the code low. I'll show a more advanced DOMDocument example later on.
So about the xpath: How to find the node and update it. First of all how to find the node:
The node has the element/tagname item. You are looking for it inside the storagehouse element, which is the root element of your XML document. All item elements in your document are expressed like this in xpath:
/storagehouse/item
From the root, first storagehouse, then item. Divided with /. You already know that, so the interesting part is how to only take those item elements that have the specific ID. For that the predicate is used and added at the end:
/storagehouse/item[#id="id"]
This will return all item elements again, but this time only those which have the attribute id with the value id (string). For example in your case with the following XML:
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<storagehouse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="schema.xsd">
<item id="c7278e33ef0f4aff88da10dfeeaaae7a">
<name>HDMI Cable 3m</name>
<weight>0.5</weight>
<category>Cables</category>
<location>B3</location>
</item>
<item id="df799fb47bc1e13f3e1c8b04ebd16a96">
<name>Dell U2410</name>
<weight>2.5</weight>
<category>Monitors</category>
<location>C2</location>
</item>
</storagehouse>
XML;
that xpath:
/storagehouse/item[#id="df799fb47bc1e13f3e1c8b04ebd16a96"]
will return the computer monitor (because such an item with that id exists). If there would be multiple items with the same id value, multiple would be returned. If there were none, none would be returned. So let's wrap that into a code-example:
$simplexml = simplexml_load_string($xml);
$result = $simplexml->xpath(sprintf('/storagehouse/item[#id="%s"]', $id));
if (!$result || count($result) !== 1) {
throw new Exception(sprintf('Item with id "%s" does not exists or is not unique.', $id));
}
list($item) = $result;
In this example, $titem is the SimpleXMLElement object of that computer monitor xml element name item.
So now for the changes, which are extremely easy with SimpleXML in your case:
$item->category = 'LCD Monitor';
And to finally see the result:
echo $simplexml->asXML();
Yes that's all with SimpleXML in your case.
If you want to do this with DOMDocument, it works quite similar. However, for updating an element's value, you need to access the child element of that item as well. Let's see the following example which first of all fetches the item as well. If you compare with the SimpleXML example above, you can see that things not really differ:
$doc = new DOMDocument();
$doc->loadXML($xml);
$xpath = new DOMXPath($doc);
$result = $xpath->query(sprintf('/storagehouse/item[#id="%s"]', $id));
if (!$result || $result->length !== 1) {
throw new Exception(sprintf('Item with id "%s" does not exists or is not unique.', $id));
}
$item = $result->item(0);
Again, $item contains the item XML element of the computer monitor. But this time as a DOMElement. To modify the category element in there (or more precisely it's nodeValue), that children needs to be obtained first. You can do this again with xpath, but this time with an expression relative to the $item element:
./category
Assuming that there always is a category child-element in the item element, this could be written as such:
$category = $xpath->query('./category', $item)->item(0);
$category does now contain the first category child element of $item. What's left is updating the value of it:
$category->nodeValue = "LCD Monitor";
And to finally see the result:
echo $doc->saveXML();
And that's it. Whether you choose SimpleXML or DOMDocument, that depends on your needs. You can even switch between both. You probably might want to map and check for changes:
$repository = new Repository($xml);
$item = $repository->getItemByID($id);
$item->category = 'LCD Monitor';
$repository->saveChanges();
echo $repository->getXML();
Naturally this requires more code, which is too much for this answer.

Categories