I've never asked a question here before so please forgive my question if its formatted badly or not specific enough. I am just a dabbler and know very little about PHP and XPath.
I have an XML file like this:
<catalogue>
<item>
<reference>A1</reference>
<title>My title1</title>
</item>
<item>
<reference>A2</reference>
<title>My title2</title>
</item>
</catalogue>
I am pulling this file using SimpleXML:
$file = "products.xml";
$xml = simplexml_load_file($file) or die ("Unable to load XML file!");
Then I am using the reference from a URL parameter to get extra details about the 'item' using PHP:
foreach ($xml->item as $item) {
if ($item->reference == $_GET['reference']) {
echo '<p>' . $item->title . '</p>';
}
So from a URL like www.mysite.com/file.php?reference=A1
I would get this HTML:
<p>My title1</p>
I realise I might not be doing this right and any pointers to improving this are welcome.
My question is, I want to find the next and previous 'item' details. If I know from the URL that reference=A1, how do I find the reference, title etc of the next 'item'? If I only have 'A1' and I know that's a reference node, how do I get HTML like this:
<p>Next item is My title2</p>
I have read about following-sibling but I don't know how to use it. I can only find the following-sibling of the reference node, which isn't what I need.
Any help appreciated.
You could use:
/catalogue/item[reference='A1']/following-sibling::item[1]/title
Meaning: from an item element child of catalogue root element, having a reference element with 'A1' string value, navegate to first following sibling item element's title child.
I´d probably use xpath to fetch the next/previous (and current) node.
<?php
error_reporting(E_ALL ^ E_NOTICE);
$s = '
<catalogue>
<item>
<reference>A1</reference>
<title>My title1</title>
</item>
<item>
<reference>A2</reference>
<title>My title2</title>
</item>
<item>
<reference>A3</reference>
<title>My title3</title>
</item>
</catalogue>
';
$xml = simplexml_load_string($s);
$reference = 'A3';
list($current) = $xml->xpath('/catalogue/item[reference="' . $reference . '"]');
if($current) {
print 'current: ' . $current->title . '<br />';
list($prev) = $current->xpath('preceding-sibling::*[1]');
if($prev) {
print 'prev: ' . $prev->title . '<br />';
}
list($next) = $current->xpath('following-sibling::*[1]');
if($next) {
print 'next: ' . $next->title . '<br />';
}
}
See the documentation of SimpleXMLElement::xpath and XPath syntax documentation.
Related
I have a xml file like this :
<rss version="2.0" xmlns:atom="https://www.w3.org/2005/Atom">
<channel>
<item>
<city>London</city>
<description>Trip</description>
<link>page.php</link>
<img>img.jpg</img>
</item>
<item>
<city>London</city>
<description>Trip</description>
<link>page.php</link>
<img>img.jpg</img>
</item>
<item>
<city>Paris</city>
<description>Trip</description>
<link>page.php</link>
<img>img.jpg</img>
</item>
.
.
</channel>
</rss>
If I want to select TRIP in LONDON, I do that :
<?php
$xml = simplexml_load_file('file.xml');
$items = $xml->xpath('//item[city[contains(.,"London")] and description[contains(.,"Trip")]]');
foreach($items as $item){
echo ' txt ';
}
?>
If I want to select ONLY the first TRIP in LONDON, I do that :
<?php
$xml = simplexml_load_file('file.xml');
$items = $xml->xpath('//item[city[contains(.,"London")] and description[contains(.,"Trip")]]')[0];
foreach($items as $item){
echo ' txt ';
}
?>
I try also 1 instead of 0, and this
[position()=0]
it does not work.
What's wrong ?
I keep looking.
I have made several tests only with the position filter, for example :
<?php
$xml = simplexml_load_file('site-alpha.xml');
$items = $xml->xpath('//(/item)[1]');
foreach($xml->channel->item as $item){
echo '<div>....</div>';
}
?>
And it doesn't work.
I think I have a problem with this part, but I don't see where.
<?php
// Load the XML file
$xml = simplexml_load_file('your_xml_file.xml');
// Iterate through each "item" element
foreach ($xml->item as $item) {
// Output the description and city
echo $item->description . ' in ' . $item->city . '<br>';
}
?>
Unlike php, xpath indexing start from "1".
So either of these should get you only the first trip:
#indexing is indicated inside the xpath expression so it starts with 1:
$items = $xml->xpath('//item[city[contains(.,"London")] and description[contains(.,"Trip")]][1]');
or
#indexing is indicated outside the xpath expression so it's handled by php and starts with 0:
$items = $xml->xpath('//item[city[contains(.,"London")] and description[contains(.,"Trip")]]')[0];
Consider an XML file like this :
<title>sometitle</title>
<a>
<abc>content1</abc>
<xyz>content2</sxyz>
<metadata>
<b>
<c>content3</c>
<d><attribute></d>
</b>
</metadata>
</a>
I use this code to parse my file and i get the output such as :
title : abc
a:content1 content2 content 3
i.e it only parses the first level tags and fails to parse subtags and get the value ,any help is much appreciated since I'am a complete newbie in this.So far this is what I have tried:
$xmlDoc = new DOMDocument();
$xmlDoc->load("somedoc.xml");
$x = $xmlDoc->documentElement;
foreach($x->childNodes AS $item)
{
print $item->nodeName . " = " . $item->nodeValue . "<br>";
}
Check the below link for php documentation on haschildnodes() functions.
You can see samples in the below page for usage.
http://php.net/manual/en/domnode.haschildnodes.php
In all the examples of simplexml I have seen the structure of the xml is like:
<examples>
<example>
</example>
<example>
</example>
<example>
</example>
</examples>
However I am dealing with xml in the form:
<examples>
<example>
</example>
<example>
</example>
<example>
</example>
</examples>
<app>
<appdata>
<error>
<Details>
<ErrorCode>101</ErrorCode>
<ErrorDescription>Invalid Username and Password</ErrorDescription>
<ErrorSeverity>3</ErrorSeverity>
<ErrorSource />
<ErrorDetails />
</Details>
</error>
<items>
<item>
</item>
<item>
</item>
</items>
</appdata>
</app>
I would like to skip the examples stuff, and go straight to the app tag and check if the error errorcode exists and if it doesn't, go to the items array and loop through it.
My current way of handling this is:
$items = new SimpleXMLElement($xml_response);
foreach($items as $item){
//in here I check the presence of the object properties
}
Is there a better way? The problem is the xml structure sometimes changes order so I want to be able to go straight to particular parts of the xml.
This kind of thing is very easy using XPath, and handily, SimpleXML has an xpath function built into it! XPaths allow you to select nodes in a graph based on their ancestors, descendants, attributes, values, and so on.
Here is an example of using SimpleXML's xpath function to extract data from your XML. Note that I added an extra parent element to the sample you posted so that the XML would validate.
$sxo = new SimpleXMLElement($xml);
# this selects all 'error' elements with parent 'appdata', which has parent 'app'
$error = $sxo->xpath('//app/appdata/error');
if ($error) {
# go through the error elements...
while(list( , $node) = each($error)) {
# get the error details
echo "Found an error!" . PHP_EOL;
echo $node->Details->ErrorCode
. ", severity " . $node->Details->ErrorSeverity
. ": " . $node->Details->ErrorDescription . PHP_EOL;
}
}
Output:
Found an error!
101, severity 3: Invalid Username and Password
Here's another example -- I edited the XML excerpt slightly to show the results better here:
// edited <items> section of the XML you posted:
<items>
<item>Item One
</item>
<item>Item Two
</item>
</items>
# this selects all 'item' elements under appdata/items:
$items = $sxo->xpath('//appdata/items/item');
foreach ($items as $i) {
echo "Found item; value: " . $i . PHP_EOL;
}
Output:
Found item; value: Item One
Found item; value: Item Two
There's more information in the SimpleXML XPath documentation, and try the zvon.org XPath tutorials -- they give a good grounding in XPath 1.0 syntax.
I have an rss feed that is formatted something like this:
<item>
<posturl>mysite.com/abc.html</posturl>
<imagepath>123.jpg</imagepath>
</item>
<item>
<posturl>mysite.com/def.html</posturl>
<imagepath>456.jpg</imagepath>
</item>
<item>
<posturl>mysite.com/ghi.html</posturl>
<imagepath>789.jpg</imagepath>
</item>
I have a script where I want to extract both the values above. I can extract one of the values and build html code using code like:
<?php
$xml = simplexml_load_file('http://myrssfeed.com');
$imgs = $xml->xpath('//imagepath');
echo '<ul>';
foreach($imgs as $output1) {
echo '<li><img src="' . $output1 . '" ></li>';
}
echo '</ul>';
?>
This works like a charm thanks to all the help from people here. But I need to expand that second echo line to read something like:
echo '<li><img src="' . $output1 . '" ></li>';
I need help on how to extract $output2 = posturl within the same item as $output1.
Basically I need to display the images as links... both imagepath and posturl are elements within item.
Many thanks.
You could select the <item> elements instead of <posturl> and <imagepath>. Like this:
$xml = simplexml_load_file('http://myrssfeed.com');
echo '<ul>';
foreach($xml->xpath('//item') as $item) {
printf('<li><img src="%s" /></li>',
$item->posturl, $item->imagepath);
}
echo '</ul>';
how can i get the values of a,b and c from the following xml code?
<result name="response" f="139" c="0">
−
<doc score="5.06756" pos="0">
<snippet name="a" highlighted="yes">example</snippet>
<snippet name="b" highlighted="yes">bexample</snippet>
<snippet name="c">cexample</snippet>
</doc>
</result>
I tried to print the nodes, but It failed:
$xmlDoc = new DOMDocument();
$xmlDoc->load($content);
$x = $xmlDoc->documentElement;
foreach ($x->childNodes AS $item)
{
print $item->nodeName . " = " . $item->nodeValue . "<br />";
}
Can anyone tell me how I can parse it? I cannot use simple xml, so I am moving to Dom.
Using DOM allows you to use several distinct ways of extracting informations.
For example, you could work with :
DOMDocument::getElementsByTagName, to extract all tags having a specific name.
Or DOMXPath, for more complex queries.
As an example, here's a portion of code that demonstrates how to use the first solution to extract all <snippet> tags :
$snippets = $xmlDoc->getElementsByTagName('snippet');
foreach ($snippets as $tag) {
echo $tag->getAttribute('name') . ' = ' . $tag->nodeValue . '<br />';
}
And you'd get this output :
a = example
b = bexample
c = cexample
And, as another example, here's a solution that demonstrates how to use the second solution, to do a more complex query on the XML data -- here, extracting the <snippet> tag that as it's name attribute with a value of a :
$xpath = new DOMXPath($xmlDoc);
$snippetsA = $xpath->query('//snippet[#name="a"]');
if ($snippetsA->length > 0) {
foreach ($snippetsA as $tag) {
echo $tag->getAttribute('name') . ' = ' . $tag->nodeValue . '<br />';
}
}
Which only gets you one result -- the corresponding tag :
a = example
Starting from here, the possibilities are almost limitless ;-)