I need to create a search of string in an XML file on all nodes.
---catalog.xml---
<?xml version="1.0" encoding="ISO-8859-1"?>
<Catalog>
<Category>
<Name>Biscuit</Name>
<Location>
<Id>Butter</Id>
<Description>The butter biscuit cost $10 per pack</Description>
</Location>
<Location>
<Id>Chocolate</Id>
<Description>The chocolate biscuit cost $20 per pack</Description>
</Location>
</Category>
<Category>
<Name>Cake</Name>
<Location>
<Id>Cup</Id>
<Description>This is a cup cake</Description>
</Location>
<Location>
<Id>Slice</Id>
<Description>This is a slice cake</Description>
</Location>
</Category>
</Catalog>
---search.php---
<?php
$catalog = simplexml_load_file("catalog.xml");
$category = $catalog->Category;
$location = $category->Location;
foreach($location->Description as $desc)
{
$string = string($desc);
$find = 'chocolate';
$result = strpos($string, $find)
if ($result !== false)
{
echo $result;
}
else
{
echo "No Result";
}
}
?>
The error I received is:
Parse error: syntax error, unexpected T_IF on line 14
Since there is "chocolate" at node and Node, I need to display both node result.
----New Amended Code / 20 Nov 2012---
<?php
$catalog = simplexml_load_file("catalog.xml");
$find = "chocolate";
$lcFind = strtolower($find);
$ll = implode('', range('a', 'z'));
$ul = strtoupper($ll);
$xpath_result = $catalog->xpath("//*[contains(translate(text(), '$ul','$ll'),'$lcFind')]");
if ($xpath_result) {
foreach ($xpath_result as $res) {
$category = $catalog->Category;
$name = $category->Name;
$loc = $category->Location;
$id = $loc->Id;
echo "Category: ", $name, "<br />";
echo "ID: ", $id, "<br />";
echo "Description :", $res, "<br />";
}
}
else {
echo "No matching descriptions found for word '<i>$find</i>'<br />";
}
?>
Result (Wrong):
Category: Biscuit
ID: Butter //This should be "Chocolate"
Description :Chocolate //This should be the description for "Chocolate"
Category: Biscuit
ID: Butter
Description :The chocolate biscuit cost $20 per pack
It should be...
$result = strpos($string, $find);
You should finish your statements with semicolons in PHP. And it'd probably be quite a nice idea using IDE (or even a text editor) with PHP syntax check.
... Yet there's only the beginning of the problem (and I'm not talking about non-existing string function here). See, with this code you're trying to search for some term only in descriptions of the first location of the first category. If that's actually the task, ok, but somehow I feel your original intent is better expressed with this:
$found = false;
foreach ($catalog->Category as $category) {
foreach($category->Location as $location) {
$description = "{$location->Description}";
$result = strpos($description, $find);
if ($result !== FALSE) {
echo "Word '<i>$find</i>' found in <b>$description</b> at position " . ($result + 1) . '.<br />';
$found = true;
}
}
}
if (! $found) {
echo "No matching descriptions found for word '<i>$find</i>'<br />";
}
And even this can be optimized with XPath - especially if you actually don't need to echo the position:
$xpath_result = $catalog->xpath("//Description[contains(text(),'$find')]");
if ($xpath_result) {
foreach ($xpath_result as $res) {
echo "Word '<i>$find</i>' found in <b>$res</b><br />";
}
}
else {
echo "No matching descriptions found for word '<i>$find</i>'<br />";
}
... or, for case-insensitive search:
$lcFind = strtolower($find);
$ll = implode('', range('a', 'z'));
$ul = strtoupper($ll);
$xpath_result = $catalog->xpath("//*[contains(translate(text(), '$ul', '$ll'),'$lcFind')]");
... // the rest of code is the same
Related
I have to create a live search on a website. I have to use PHP, but I have never studied it and I am pretty much a beginner in programming.
I have an XML-file as a database and the aim is to get node values and display them as suggestions as a user types in something. The problem with this code is that it spits out all of the node values onto the web page.
Here is the PHP code:
<?php
$xmlDoc = new DOMDocument();
$xmlDoc->load("collection.xml");
$books = $xmlDoc->getElementsByTagName("book");
$q = $_GET["q"];
if (strlen($q) > 0) {
$hint = "";
foreach ($books as $book) {
$name = $book->getElementsByTagName("name")->item(0)->nodeValue;
echo "$name <br/>";
}
}
if ($hint == "")
{
$response="no suggestion";
}
else
{
$response=$hint;
}
//output the response
echo $response;
?>
Here is the XML-file:
<books>
<book>
<name>Harry Potter</name>
<quantity> 50 </quantity>
<price>19.90</price>
</book>
<book>
<name>Casino Royale</name>
<quantity> 50 </quantity>
<price>12.99</price>
</book>
<book>
<name>The Great Gatsby</name>
<quantity> 40 </quantity>
<price>14.90</price>
</book>
</books>
Can someone please help me fix this issue so that I can continue working on my project. Thank you in advance for your time and help! Aprreciate it a lot!
The issue is here:
$hint = "";
foreach ($books as $book) {
$name = $book->getElementsByTagName("name")->item(0)->nodeValue;
echo "$name <br/>";
}
Notice that you have a "foreach loop" here. The "$name=$book...." line simply reads the value of that particular XML node and assigns it to the $name variable. Then you are doing a call to echo $name. So in essence, all you're doing here is reading the value of the XML node and printing it. No part of your code compares the $name to your search query ($q). It seems that what you want to happen is only print out books that somehow match $q.
In order to do that we need to apply some logic to your foreach loop to only print out values that match $q.
Here is a suggestion:
$hint = "";
foreach ($books as $book) {
$name = $book->getElementsByTagName("name")->item(0)->nodeValue;
// Let's only show this book if $q appears somewhere in $name.
if (strpos($name, $q) !== false && strpos($name, $q) >= 0)
{
echo $name . "<br />";
}
}
I am having a hard time trying to understand why I can't compare the values of two arrays in PHP. If I echo both of these during the loop using "echo $description->ItemDesriptionName;" and "echo $item->ItemName;" the values seem to show as the same, but when I try to compare them using if, nothing works. What am I missing?
<?php
$xml=simplexml_load_file("test.xml") or die("Error: Cannot create object");
$categories = $xml->Menu->Categories;
$items = $xml->Menu->Categories->Items->ItemObject;
$itemdescription = $xml->Menu->Options->Description->DescriptionObject;
foreach($items as $item) {
echo $item->ItemName . ' - ' . $item->Price . '</br>';
foreach ($itemdescription as $description) {
if ($description->ItemDescriptionName == $item->ItemName) {
echo 'We have a match!';
//where I would echo $description->ItemDescription;
}
}
}
?>
Here is the XML file
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Menu>
<Categories>
<Name>Category 1</Name>
<Items>
<ItemObject>
<ItemName>Item 1</ItemName>
<Price>1</Price>
</ItemObject>
<ItemObject>
<ItemName>Item 2</ItemName>
<Price>3</Price>
</ItemObject>
</Items>
</Categories>
<Options>
<Description>
<DescriptionObject>
<ItemDescriptionName>Item 1</ItemDescriptionName>
<ItemDescription>A Great item</ItemDescription>
</DescriptionObject>
<DescriptionObject>
<ItemDescriptionName>Item 2</ItemDescriptionName>
<ItemDescription>A Great item as well</ItemDescription>
</DescriptionObject>
</Description>
</Options>
</Menu>
</Root>
compare as string
and you have typo in ItemDescriptioName (ItemDescriptionName)
if ( (string)$description->ItemDescriptionName == (string)$item->ItemName) {
Convert to string and then compare
<?php
$xml=simplexml_load_file("test.xml") or die("Error: Cannot create object");
$menu = $xml->Menu;
$categories = $xml->Menu->Categories;
$items = $xml->Menu->Categories->Items->ItemObject;
$itemdescription = $xml->Menu->Options->Description->DescriptionObject;
foreach($items as $item) {
$itemname = $item->ItemName;
foreach ($itemdescription as $description) {
$descriptionname = $description->ItemDescriptionName ;
echo $itemname." ---- ".$descriptionname."<br/>";
if((string)$itemname === (string)$descriptionname){
echo "Yes its matched";
}
}
}
?>
Working fine for me
The properties like $description->ItemDescriptionName are SimpleXMLElement objects. So you do not compare strings but two objects.
SimpleXMLElement objects implement the magic method __toString(). They can be cast to string automatically, but a compare between to objects will not trigger that. You can force it:
if ((string)$description->ItemDescriptionName === (string)$item->ItemName) {
...
Can you access them directly instead using an accordant index?
...
$items = $xml->Menu->Categories->Items->ItemObject;
$itemdescription = $xml->Menu->Options->Description;
$i = 0;
foreach ($items as $item) {
echo $i.' '.$item->ItemName . ' - ' . $item->Price;
echo $itemdescription->DescriptionObject[$i]->ItemDescriptionName[0];
echo ' ';
echo $itemdescription->DescriptionObject[$i]->ItemDescription[0];
echo '</br>';
$i++;
}
I am trying to display attributes foreach global_ivr_variable:
$xml = '
<response method="switchvox.ivr.globalVariables.getList">
<result>
<global_ivr_variables>
<global_ivr_variable id="1" name="cid_name" value="Smith" />
<global_ivr_variable id="2" name="Q_ID_Global" value="COS" />
</global_ivr_variables>
</result>
</response>
';
$sxml = simplexml_load_string($xml);
foreach($sxml->result->global_ivr_variables->global_ivr_variable->attributes() as $a => $b)
{
echo $a .'=' . $b . "<br>";
}
All I get is the attributes of the first node:
id="1"
name="cid_name"
value="Smith"
I've also tried the following, which gives me no values at all...
foreach($sxml->result->global_ivr_variables as $xvar)
{
$a = $xvar->global_ivr_variable->id;
$b = $xvar->global_ivr_variable->name;
$c = $xvar->global_ivr_variable->value;
echo 'a='.$a.', b='.$b.', c='.$c.'<br>';
}
a=, b=, c=
Thank you all who step up to help the needy!
You almost had it but you need to iterate through each of the <global_ivr_variable> elements and then pull out the attributes:
foreach($sxml->result->global_ivr_variables->global_ivr_variable as $variable)
{
foreach($variable->attributes() as $a => $b)
{
echo $a .'=' . $b . "<br>";
}
}
If you make use of PHP variables, those problems normally easily disappear because your code is more readable.
Additionally you can access the attributes in SimpleXML via array-notation:
$variables = $sxml->result->global_ivr_variables->global_ivr_variable;
foreach ($variables as $variable)
{
printf("%s = %s\n", $variable['name'], $variable['value']);
}
This gives the following output:
cid_name = Smith
Q_ID_Global = COS
As you know upfront for which attributes you're looking for, your code is much more clear and stable by using those names.
However, if you're looking for just all attributes of the global_ivr_variable elements, then it's easier to iterate over them with XPath:
$allAttributes = $sxml->xpath('//global_ivr_variable/#*');
foreach ($allAttributes as $attribute) {
printf("%s = %s\n", $attribute->getName(), $attribute);
}
This then gives the following output:
id = 1
name = cid_name
value = Smith
id = 2
name = Q_ID_Global
value = COS
Here is the full code-example:
$xml = <<<XML
<response method="switchvox.ivr.globalVariables.getList">
<result>
<global_ivr_variables>
<global_ivr_variable id="1" name="cid_name" value="Smith" />
<global_ivr_variable id="2" name="Q_ID_Global" value="COS" />
</global_ivr_variables>
</result>
</response>
XML;
$sxml = simplexml_load_string($xml);
$variables = $sxml->result->global_ivr_variables->global_ivr_variable;
foreach ($variables as $variable)
{
printf("%s = %s\n", $variable['name'], $variable['value']);
}
echo "----\n";
$allAttributes = $sxml->xpath('//global_ivr_variable/#*');
foreach ($allAttributes as $attribute) {
printf("%s = %s\n", $attribute->getName(), $attribute);
}
We use Acalog at our institution and want to use their (unsupported) API to pull catalog content into our site from theirs. I can access their files and pull out the information, but the formatting (paragraph, bold, italics, breaks) is done as nodes (h:p, h:b, h:i, h:br). Unfortunately, the text I've pulled from searching for a:content only brings straight text and does not include the formatting nodes. How can I bring the nodes with the text? Where am I going wrong?
The start of the XML (I broke it off at about the half mark)
<catalog xmlns="http://acalog.com/catalog/1.0" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:a="http://www.w3.org/2005/Atom" xmlns:xi="http://www.w3.org/2001/XInclude" id="acalog-catalog-6">
<hierarchy>
<legend>
<key id="acalog-entity-type-5">
<name>Department</name>
<localname>Department</localname>
</key>
</legend>
<entity id="acalog-entity-239">
<type xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" xi:xpointer="xmlns(c=http://acalog.com/catalog/1.0) xpointer((//c:key[#id='acalog-entity-type-5'])[1])"/>
</type>
<a:title xmlns:a="http://www.w3.org/2005/Atom">American Studies</a:title>
<code/>
<a:content xmlns:a="http://www.w3.org/2005/Atom" xmlns:h="http://www.w3.org/1999/xhtml">
<h:p xmlns:h="http://www.w3.org/1999/xhtml">
<h:span class="dept_intro">
<h:i>Chair of the Department of American Studies: </h:i>
</h:span>
<h:span class="dept_intro">John Smith</h:span>
<h:br/>
<h:span class="dept_intro">
<h:br/>
Professors: Jane Smith; Sarah Smith, <h:i class="dept_intro">The Douglas Family Chair in American Culture, History, and Literary and Interdisciplinary Studies</h:i>
<h:br/><h:br/>
Associate Professor: Michael Smith
</h:span>
<h:span class="dept_intro"><h:br/></h:span>
</h:p>
<h:p xmlns:h="http://www.w3.org/1999/xhtml">
<h:span class="dept_intro">Assistant Professor: Rebecca Smith</h:span>
</h:p>
<h:p xmlns:h="http://www.w3.org/1999/xhtml">
<h:span class="dept_intro">Lecturer: * Leonard Smith</h:span></h:p>
<h:p xmlns:h="http://www.w3.org/1999/xhtml">
<h:span class="dept_intro">Visiting Lecturer: * Robert Smith<h:br/><h:br/><h:br/><h:br/></h:span><h:strong>Department Overview</h:strong></h:p>
<h:p xmlns:h="http://www.w3.org/1999/xhtml" class="MsoNormal">American studies is an interdiscipl
Here's the code I've written thus far:
$xml = file_get_contents($url);
if ($xml === false) {
return false;
} else {
// Create an empty DOMDocument object to hold our service response
$dom = new DOMDocument('1.0', 'UTF-8');
// Load the XML
$dom->loadXML($xml);
// Create an XPath Object
$xpath = new DOMXPath($dom);
// Register the Catalog namespace
$xpath->registerNamespace('h', 'http://www.w3.org/1999/xhtml');
$xpath->registerNamespace('a', 'http://www.w3.org/2005/Atom');
$xpath->registerNamespace('xi', 'http://www.w3.org/2001/XInclude');
// Check for error
$status_elements = $xpath->query('//c:status[text() != "success"]');
if ($status_elements->length > 0) {
// An error occurred
return false;
}
$x = $dom->documentElement;
foreach ($x->childNodes AS $item)
{
//echo $item->nodeName . " = " . $item->nodeValue . "<br/><br />";
}
// Retrieve all catalogs elements
$pageText = $xpath->query('//a:content');
if ($pageText->length == 0) {
// No text found
return false;
}
foreach ($pageText AS $item) {
$txt = (string) $item->nodeValue;
$txt = str_replace('<h:i>','<i>',$txt);
$txt = str_replace('</h:i>','</i>',$txt);
$txt = str_replace('<h:span class="dept_intro">','<p>',$txt);
$txt = str_replace('</h:span>','</p>',$txt);
if(strpos($txt,'Department Overview')) {
echo '<p>' . str_replace('Department Overview','',$txt) . '</p>';
break;
} else {
echo '<p>' . $txt . '</p>';
}
//echo $pageText->nodeValue;
}
}
The line $pageText = $xpath->query('//a:content'); pulls the content, but not the tags.
Hi and thanks for looking.
I'm trying to get just the value in brackets after 1 GBP = USD from the following link, how do I do this with SimpleXML?
http://www.sloomedia.com/currency/feeds/GBP.xml
Thanks,
Ben.
You can try with xpath :
$xml = new SimpleXMLElement($string);
/* On cherche <item><title> */
$result = $xml->xpath('/item/title');
while(list( , $node) = each($result)) {
$matches = array();
preg_match('/\((.*)\)/', $node, $matches);
//do wathever you need with result
//echo $matches[1];
}
I haven't tested the regexp quickly written, you can find something better or use substr as the length of '1GBP = XXX' is constant in your file
In order to "isolate" (or rather, select) the right node, you can use the XPath function starts-with().
After selecting the node, you still have to parse its contents. For that, you can use any kind of string-manipulation function in PHP.
$rss = simplexml_load_file('http://www.sloomedia.com/currency/feeds/GBP.xml');
$nodes = $rss->xpath('//item[starts-with(title, "1 GBP = USD")]');
if (empty($nodes))
{
die('No GBP = USD nodes');
}
// Now you got the right node in $nodes[0], you just have to extract the value in parentheses
// The simple way -- 13 is the number of characters in "1 GBP = USD ("
$usd = substr($nodes[0]->title, 13, -1);
// The flexible way
if (!preg_match('#\\(([0-9]+\\.[0-9]+)\\)#', $nodes[0]->title, $m))
{
die('Could not parse the value');
}
$usd = $m[1];
// If you want to parse every item
foreach ($rss->channel->item as $item)
{
if (!preg_match('#1 ([A-Z]+) = ([A-Z]+) \\(([0-9]+\\.[0-9]+)\\)#', $item->title, $m))
{
echo 'Could not parse ', $item->title, "\n";
continue;
}
echo '1 ', $m[1], ' = ', $m[3], ' ', $m[2], "\n";
}