SimpleXMLElement::xpath

(PHP 5, PHP 7, PHP 8)

SimpleXMLElement::xpath在 XML 数据上运行 XPath 查询

描述

public SimpleXMLElement::xpath(string $expression): array|null|false

xpath 方法在 SimpleXML 节点中搜索与 XPath expression 匹配的子节点。

参数

expression

XPath 路径

返回值

如果成功,则返回 SimpleXMLElement 对象的 array;如果出现错误,则返回 nullfalse

示例

示例 #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);

/* 搜索 <a><b><c> */
$result = $xml->xpath('/a/b/c');

foreach (
$result as $node) {
echo
'/a/b/c: ',$node,"\n";
}

/* 相对路径也可以工作... */
$result = $xml->xpath('b/c');

foreach (
$result as $node) {
echo
'b/c: ',$node,"\n";
}
?>

上面的示例将输出

/a/b/c: text
/a/b/c: stuff
b/c: text
b/c: stuff

请注意,这两个结果是相等的。

参见

添加笔记

用户贡献笔记 18 个笔记

leonjanzen at gmail dot com
9 年前
要在具有命名空间的 XML 文档上运行 xpath 查询,必须在运行查询之前使用 SimpleXMLElement::registerXPathNamespace() 注册命名空间。如果 XML 文档命名空间不包含前缀,则必须创建一个任意前缀,然后在查询中使用它。

<?php
$strXml
= <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<mydoc xmlns="http://www.url.com/myns">
<message>Test message</message>
</mydoc>
XML;

$xmlDoc=new \SimpleXMLElement($strXml);

foreach(
$xmlDoc->getDocNamespaces() as $strPrefix => $strNamespace) {
if(
strlen($strPrefix)==0) {
$strPrefix="a"; // 为命名空间分配一个任意的前缀。
}
$xmlDoc->registerXPathNamespace($strPrefix,$strNamespace);
}

print(
$xmlDoc->xpath("//a:message")[0]); // 在查询中使用任意的命名空间前缀。
?>

这将输出

测试消息
grummfy at gmail dot com
14 年前
在具有命名空间的 xml 中,您需要在 xpath 请求之前执行此操作(否则将返回空数组)

<?php
$string
= str_replace('xmlns=', 'ns=', $string); //$string 是一个包含 xml 的字符串...
?>
drewish at katherinehouse dot com
19 年前
xpath() 也可以用于根据属性选择元素。有关良好的 XPath 参考,请查看:https://w3schools.org.cn/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']");

// 打印数组中的第一个(也是唯一一个)成员
echo $result[0]->asXml();
?>

该脚本将打印
<size label="Large" width="112" height="69"/>
Filippo Vicari
1 年前
您还可以搜索具有条件的兄弟节点。例如,假设需要此 XML 文件中两个兄弟节点标签的所需语言的标题

<?xml version="1.0" encoding="UTF-8"?>
<IDOC BEGIN="1">
...
<PRODUCT SEGMENT="1">
<PRODUCTCODE>005</PRODUCTCODE>
<LANG>E</LANG>
<TITLE>Name</TITLE>
<LANG_ISO>EN</LANG_ISO>
</PRODUCT>
<PRODUCT SEGMENT="1">
<PRODUCTCODE>005</PRODUCTCODE>
<LANG>I</LANG>
<TITLE>Name I</TITLE>
<LANG_ISO>IT</LANG_ISO>
</PRODUCT>
...
</IDOC>

让我们分解它
<?php
"//PRODUCT" // 查找 product 标签
"//PRODUCT/LANG[.='E']" // 在其中查找 lang == "E"
"//PRODUCT/LANG[.='E']/../" // 向上移动一级
"//PRODUCT/LANG[.='E']/../TITLE" // 获取 title 标签内容

// 组合搜索并查找兄弟节点 ----------------------------
$xml = simplexml_load_file("fname.xml");

// 搜索 TITLE 字段,其兄弟节点 == "E" ----------
$title_E_array = $xml->xpath("//PRODUCT/LANG[.='E']/../TITLE"); // 默认情况下,返回 SimpleXmlElement 的数组
$title = (string) $xml->xpath("//PRODUCT/LANG[.='E']/../TITLE")[0]; // 以这种方式仅保存值作为字符串
?>
anemik
16 年前
如果您想轻松地查找满足 XML 数据中某些条件的所有记录,例如

....
<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>午夜雨</title>
<genre>奇幻</genre>
<price>5.95</price>
</book>
...

尝试以下示例

<?php

$xmlStr
= file_get_contents('data/books.xml');
$xml = new SimpleXMLElement($xmlStr);
// 通过标签值搜索记录:
// 查找所有价格高于 40 美元的书籍记录
$res = $xml->xpath("book/price[.>'40']/parent::*");
print_r($res);

?>

您将看到以下响应
Array (
[0] => SimpleXMLElement Object
(
[@attributes] => Array
(
[id] => bk101
)

[author] => Gambardella, Matthew
[title] => XML 开发人员指南
[genre] => 计算机
[price] => 44.95
[publish_date] => 2000-10-01
[description] => 深入探讨使用 XML 创建应用程序

)
...
匿名
9 年前
如前所述,如果使用默认命名空间“xmlns”,xpath 将失败,例如在

<?php

$xmlstring
= $string = <<<XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<fileVersion appName="xl" />
</workbook>
XML;
?>

xpath 无法在没有显式指定命名空间的情况下搜索 XML。
有两种选择
1. 将“xmlns”重命名为其他内容,以欺骗 xpath 使其相信没有定义默认命名空间。
2. 注册一个字符串作为默认命名空间,并在所有查询中使用该字符串。不幸的是,空格不起作用。
在 XPath 2.0 成为默认库之前,目前不存在其他选择。
canuemail at gmail dot com
16 年前
如果您想根据一个值搜索 XML 中的多个值,那么此代码可能对您有所帮助。

如果存在
<Record>
<country>巴基斯坦</country>
<code>+92</code>
<Value>100<Value>
</Record>

那么请尝试以下代码
<?php
$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;
}

}
?>
awoerl at contentserv dot com
7 年前
我尝试在传递的 xpath 语句中使用字符串函数,例如

$oXml->xpath('substring(/foo/bar/text(), 0, 4)');

但是无论我尝试哪种语法,我都无法让字符串函数正常工作。

xpath 方法是否不支持函数调用?
yetihehe at yetihehe dot com
13 年前
Xpath 实际上知道哪个元素是根元素,无论您在哪个元素上执行它。示例

<?php

$string
= <<<XML
<a>
<b>
<c>text</c>
<c>stuff</c>
</b>
<b>
<c>code</c>
</b>
</a>
XML;

header('content-type: text/plain');

$xml = new SimpleXMLElement($string);

// 相对于根元素
$b0=$xml->b[0]->xpath('//c');
while(list( ,
$node) = each($b0)) {
echo
'b[0]: //c: ',$node,"\n";
}

$b1=$xml->b[1]->xpath('//c');
while(list( ,
$node) = each($b1)) {
echo
'b[1]: //c: ',$node,"\n";
}

echo
"\n";

// 相对于当前元素
$b0=$xml->b[0]->xpath('.//c');
while(list( ,
$node) = each($b0)) {
echo
'b[0]: .//c: ',$node,"\n";
}

$b1=$xml->b[1]->xpath('.//c');
while(list( ,
$node) = each($b1)) {
echo
'b[1]: .//c: ',$node,"\n";
}

?>

将返回
b[0]: //c: text
b[0]: //c: stuff
b[0]: //c: code
b[1]: //c: text
b[1]: //c: stuff
b[1]: //c: code

b[0]: .//c: text
b[0]: .//c: stuff
b[1]: .//c: code
paul at pmtlogic dot com
16 年前
xpath 似乎无法处理查询本身中嵌入的单引号。例如,我想根据国家/地区名称在 XML 文件中查找地理坐标。

xml 片段

<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>

以下代码无法正常工作

<?php
$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";
}
?>

我尝试过嵌入单引号的多种变体(即转义代码),但没有结果。W3C 也未提供任何解释。

此外,似乎无法在属性值中嵌入通配符(您可以在属性名称中嵌入通配符)。否则,以下代码在本例中可能是一个合理的替代方案

<?php $result = $xml->xpath("//zone[@country='Cote d*Ivoire']"); ?>
DrupalFocus at earthlink dot net
14 年前
看起来相对路径相对于调用 Xpath() 方法的 SimpleXMLElement 对象而言。基本上是截断了根元素。

<?php
$string
= <<<XML
<foo>
<bar>
<a>
<b>
<c>text</c>
<c>stuff</c>
</b>
<d>
<c>code</c>
</d>
</a>
</bar>
</foo>
XML;

$xml = new SimpleXMLElement($string);
/* 使用二级相对路径 <bar><a><b><c> 搜索子节点 */
$result = $xml->xpath('a/b/c');
if (
$result)
foreach(
$result as $node) {
drupal_set_message(t('a/b/c: @node.', array('@node' => $node)));
}
else
drupal_set_message(t('a/b/c: 失败.'));

/* 相对于 SimpleXMLElement,搜索二级相对路径 <bar><a><b><c>,但 foo 在哪里 */
$bar = $xml->bar;
$result = $bar->xpath('a/b/c');
foreach(
$result as $node) {
drupal_set_message(t('bar->a/b/c: @node.', array('@node' => $node)));
}
?>

结果为
#
# a/b/c: 失败.
# bar->a/b/c: text.
# bar->a/b/c: stuff.
arealzone-czernobyl at yahoo dot de
15 年前
在 XML 文档中统计节点数量的最简单方法

<?php
$xml
= new SimpleXMLElement($xmlstr);

$xmlNode = $xml->xpath('root/yourNodeName');
$nodeCount = count($xmlNode);

echo
$nodeCount;
?>
Alexis Peters
7 年前
如果您想对多维数组使用此行为,可以使用以下代码

<?php
function apath(&$a, $path, $value = NULL) {
$pathes = explode('/',$path);

if(
count($pathes) > 1) {
$firstPart = array_shift($pathes);
if(!isset(
$a[$firstPart])) {
$a[$firstPart] = array();
}
return
apath($a[$firstPart], implode('/', $pathes), $value);
} else {
if(
$value){
$a[$path] = $value;
} else {
return
$a[$path];
}
}
}

apath($a, 'user/name', array('prename' => 'foo', 'lastname' => 'bar'));

$a = array();
var_dump($a);
var_dump(apath($a, 'user/name/lastname'));

?>
结果如下

array(1) {
["user"]=>
array(1) {
["name"]=>
array(2) {
["prename"]=>
string(5) "foo"
["lastname"]=>
string(8) "bar"
}
}
}
string(8) "bar"
Hugh
18 年前
请注意,此函数并不总是返回数组。如果在文档中未找到指定的路径,它似乎会返回 false。如果您希望避免警告(并且通常是在这种情况下处理错误或控制程序流程),则需要在使用 foreach 之前检查 false 值。
behnam@zwnj
14 年前
此函数似乎无法返回 XPath string() [1] 函数的结果。例如,如果您查询 xpath("string(a/@b)"),您将获得 bool(false),尽管节点 "a" 可能会将属性 "b" 设置为非空字符串。

[1] http://www.w3.org/TR/xpath#section-String-Functions
Anti Veeranna
15 年前
对于不存在的路径,它是否返回 false 或空数组似乎取决于 libxml 版本。

我尝试了以下代码

<?php
$data
= '<foo><bar></bar></foo>';
$xml = simplexml_load_string($data);
print
gettype($xml->xpath('/first/second'));
?>

在 libxml 2.6.16 和 PHP 5.2.8 的机器上,我得到了布尔值。在 libxml 2.7.3 和 PHP 5.2.8/5.2.9 的另一台机器上,我得到了空数组。

您可以通过执行以下命令从命令行检查 libxml 版本:php --ri libxml

或者简单地查看 phpinfo() 输出
BenjaminBeck at gmx dot de
9 年前
如果您想根据一个值搜索 XML 中的多个值,那么此代码可能对您有所帮助。

如果存在
<Record>
<country>巴基斯坦</country>
<code>+92</code>
<Value>100<Value>
</Record>

那么请尝试以下代码
<?php
$sxe
= simplexml_load_file("countries.XML");
foreach(
$sxe->xpath('//RECORD') as $item) {

$v = $row->xpath('./country[. ="Pakistan"]');
if(
$v[0]){
print
$item->country;
print
$item->code;
print
$item->value;
}

}
?>
stefan at efectos dot nl
13 年前
在 XML 中存在命名空间时,通过 xpath 选择信息时遇到问题?

试试这个快速而简便的解决方案,但它确实有效

<?php
$objImageXml
= new SimpleXMLElement($objImageXml->asXML());
?>
To Top