It seems that PHP 5.2.6 also has some problems with default namespaces and xpath. If you are working with complex documents (in my case SCORM manifest XML files) and your xpath is not working, try registering the default namespace.
The XML is too long to add as an example but the snippet of code that was failing was:
<?php
...
$xpath = "//resource[@identifier='$id']";
$resourceArr = $this->xmlObj->xpath($xpath);
..
?>
This despite the resource tag being in the default namespace. Registering the namespace like this then solved the problem:
<?php
...
$this->xmlObj->registerXPathNamespace('default', 'http://www.imsglobal.org/xsd/imscp_v1p1');
$xpath = "//default:resource[@identifier='$id']";
$resourceArr = $this->xmlObj->xpath($xpath);
...
?>
SimpleXMLElement->xpath
(No version information available, might be only in CVS)
SimpleXMLElement->xpath — Runs XPath query on XML data
Description
SimpleXMLElement
array xpath
( string $path
)
The xpath method searches the SimpleXML node for children matching the XPath path .
Parameters
- path
-
An XPath path
Return Values
Returns an array of SimpleXMLElement objects or FALSE in case of an error.
Examples
Example #1 Xpath
<?php
$string = <<<XML
<a>
<b>
<c>text</c>
<c>stuff</c>
</b>
<d>
<c>code</c>
</d>
</a>
XML;
$xml = new SimpleXMLElement($string);
/* Search for <a><b><c> */
$result = $xml->xpath('/a/b/c');
while(list( , $node) = each($result)) {
echo '/a/b/c: ',$node,"\n";
}
/* Relative paths also work... */
$result = $xml->xpath('b/c');
while(list( , $node) = each($result)) {
echo 'b/c: ',$node,"\n";
}
?>
The above example will output:
/a/b/c: text /a/b/c: stuff b/c: text b/c: stuff
Notice that the two results are equal.
SimpleXMLElement->xpath
Matt
04-Jul-2008 12:35
04-Jul-2008 12:35
anemik
02-Jul-2008 07:46
02-Jul-2008 07:46
If you want to find easly all records satisfying some condition in XML data like
....
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
</book>
...
try example below
<?php
$xmlStr = file_get_contents('data/books.xml');
$xml = new SimpleXMLElement($xmlStr);
// seach records by tag value:
// find all book records with price higher than 40$
$res = $xml->xpath("book/price[.>'40']/parent::*");
print_r($res);
?>
You will see response like:
Array (
[0] => SimpleXMLElement Object
(
[@attributes] => Array
(
[id] => bk101
)
[author] => Gambardella, Matthew
[title] => XML Developer's Guide
[genre] => Computer
[price] => 44.95
[publish_date] => 2000-10-01
[description] => An in-depth look at creating applications
with XML.
)
...
Nice xpath tutorial you can find on http://www.zvon.org/xxl/XPathTutorial/General/examples.html
paul at pmtlogic dot com
18-Jun-2008 04:17
18-Jun-2008 04:17
xpath doesn't seem to be able to handle single quotes embedded in the query itself. For instance, I want to find geo coordinates in an xml file based on country name.
xml snippet:
<zones>
<zone country="Cote d'Ivoire" fullName="Yamoussoukro" geo="6.82,-5.28" id="1050"><url prefix="1001" value="fiji.html" /><url prefix="1002" value="C" /></zone>
</zones>
The following code does not work:
$xml = simplexml_load_file("my.xml");
$result = $xml->xpath("//zone[@country='Cote d\'Ivoire']");
foreach ( $result[0]->attributes() as $key => $val ) {
print "<div class='coords'>$key: $val</div>\n";
}
I have tried many variations on the embedded single quote (i.e. escape codes) but with no result. W3C offers no explanation either.
In addition, there doesn't seem to be any way of embedding wildcards in the attribute value (you can embed wildcards in the attribute name). Otherwise the following might be a reasonable substitute in this context:
$result = $xml->xpath("//zone[@country='Cote d*Ivoire']");
canuemail at gmail dot com
05-May-2008 09:05
05-May-2008 09:05
If you want to search multiple values from xml on the behalf or one value then this code can be helpfull to you.
if there is:
<Record>
<country>Pakistan</country>
<code>+92</code>
<Value>100<Value>
</Record>
then try this one:
$sxe = simplexml_load_file("countries.XML");
foreach($sxe->xpath('//RECORD') as $item) {
$row = simplexml_load_string($item->asXML());
$v = $row->xpath('//country[. ="Pakistan"]');
if($v[0]){
print $item->country;
print $item->code;
print $item->value;
}
}
fake_id_911 at yahoo dot com
02-Jan-2008 10:45
02-Jan-2008 10:45
$XML = new SimpleXMLElement("<nothing/>");
$xpath = "/a/b";
$testPath = $XML->xpath($xpath);
if(is_bool($testPath)){
print "it's a boolean!";
}
/*Xpath returns and array, and if the path is not there or there is no children, it will return an empty array which is the same as false. FALSE in this case is an empty array, not a boolean!
*/
brettz9 through yah com
14-Sep-2006 04:18
14-Sep-2006 04:18
Though it should according to XPath, the following does not work with this extension (nor apparently do other expressions not yielding a nodeset):
<?php
$paragraph_count = $page->xpath("count(//par)\");
?>
The following will work instead, in the meantime, however:
<?php
$paragraph_count = count($page->xpath(\"//par\"));
?>
brettz9 through yah
13-Sep-2006 11:31
13-Sep-2006 11:31
I was looking to find all of a given element type with attributes which were not of a particular value (including null items).
I tried this...
<?php
$xml->xpath("//pg[@show!='none']");
?>
...But that only worked if there was in fact some attribute.
I thought this should work (shouldn't it?), but it didn't:
<?php
$xml->xpath("//pg[@show!='none' or !@show]");
?>
The solution is to use the not() function:
<?php
$xml->xpath("//pg[not(@show='none')]");
?>
ron @ rotflol dot cx
29-Mar-2006 02:07
29-Mar-2006 02:07
xpath returning references to the parent simplexml object can bite you performance wise when processing large trees.
this bit me big time so looking for a solution I came up with a simple way to speed up the code.
'dereferencing' will yield instant results, code will be lighting fast.
sacrifice is losing the reference to the parent object, for simple read-only operation this should be ok.
<?php
$data = 'data.xml';
$rs = simplexml_load_file($data);
foreach($rs->xpath('//row') as $rsrow){
// 'derefence' into a seperate xml tree for performance
$row = simplexml_load_string($rsrow->asXML());
$v = $row->xpath('//field[@name="id"]');
}
?>
drewish at katherinehouse dot com
10-Jan-2006 05:40
10-Jan-2006 05:40
here's a simple work-around for the lack of support for default namespaces, just overwrite the xmlns attribute of the root element:
<?php
$xml = simplexml_load_file('http://example.com/atom_feed.xml');
// hack to work around php 5.0's problems with default namespaces and xpath()
$xml['xmlns'] = '';
// now our queries work:
$titles = $xml->xpath('entry/title');
foreach ((array)$titles as $title) {
print "$title\n";
}
?>
Hugh
18-Dec-2005 09:23
18-Dec-2005 09:23
Note that this function does not ALWAYS return an array. It seems that if the specified path is not found in the document, it will return false. You need to check for the false value before using foreach, if you wish to avoid a warning (and often to handle errors or control program flow in this situation).
retardis at gmail dot com
24-Aug-2005 05:21
24-Aug-2005 05:21
Xpath actually returns an array full of references to elements in the parent SimpleXML object. Therefore, in addition to preserving attributes, you can actually modify them and the result will be reflected in the parent.
Example:
<?php
$xml_str = <<<END
<a>
<b attr="foo"/>
</a>
END;
$xml = simplexml_load_string ($xml_str);
$result = $xml->xpath ('/a/b'); // returns an array with one element
$result[0]['attr'] = 'bar';
print $xml->asXML ();
?>
Outputs:
<a>
<b attr="bar"/>
</a>
drewish at katherinehouse dot com
10-Jul-2005 10:16
10-Jul-2005 10:16
xpath() can also be used to select elements by their attributes. For a good XPath reference check out: http://www.w3schools.com/xpath/xpath_syntax.asp
<?php
$string = <<<XML
<sizes>
<size label="Square" width="75" height="75" />
<size label="Thumbnail" width="100" height="62" />
<size label="Small" width="112" height="69" />
<size label="Large" width="112" height="69" />
</sizes>
XML;
$xml = simplexml_load_string($string);
$result = $xml->xpath("//size[@label='Large']");
// print the first (and only) member of the array
echo $result[0]->asXml();
?>
The script would print:
<size label="Large" width="112" height="69"/>
nelson dot menezes at yahoo dot co dot uk
16-Feb-2005 07:56
16-Feb-2005 07:56
Regarding the note about the return of this function "not retaining tag attributes". This is not true at all (PHP 5.0.3); The attributes are kept for each member of the array returned. So this:
<a>
<b value="1">xxx1</b>
<b value="2">xxx2</b>
</a>
An xpath of "/a/b" would return an array with two SimpleXMLElements; each can have their tag properties read like so:
$array_element['value']
dummyusr at hm dot com
26-Nov-2004 06:00
26-Nov-2004 06:00
well after testing my conclusion, if you are not interested in namespaces, is:
<?
$sxmltxt=file_get_contents($url);
$sxmltxt=str_replace("xmlns=","a=",$sxmltxt);
$xmltxt=simplexml_load_string($sxmltxt);
?>
then <? $xmltxt->xpath("//node"); ?> will work
not getting that void array
tyler.bell[at]oxarchdigital[dot]com
03-Sep-2004 12:53
03-Sep-2004 12:53
Note: xpath currently (5.0) does not work with xml documents that employ default namespaces, but a fix should be added via registerXPathNamespace() in 5.1. See http://blog.bitflux.ch/p1746.html for more info.
matt@roughest[d0t]net
03-Sep-2004 04:17
03-Sep-2004 04:17
This may be obvious, but in case you're wondering, the array resulting from the xpath() operation doesn't retain any of the nodes' attributes.
In other words, it won't return any of the tags' attributes
