PHP Conference Japan 2024

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

请注意,这两个结果相同。

参见

添加注释

用户贡献的注释 10 条

35
leonjanzen at gmail dot com
10 年前
要对具有命名空间的 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]); //在查询中使用任意的命名空间前缀。
?>

这将输出

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

<?php
$string
= str_replace('xmlns=', 'ns=', $string); //$string 是包含 xml 的字符串...
?>
18
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"/>
2
Filippo Vicari
2 年前
您还可以使用条件搜索兄弟节点。例如,假设您需要此 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/LANG[.='E']" // 在其中查找 lang == "E"
"//PRODUCT/LANG[.='E']/../" // 向上移动一级
"//PRODUCT/LANG[.='E']/../TITLE" // 获取标题标签内容

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

// 搜索兄弟节点为 == "E" 的 TITLE 字段 ----------
$title_E_array = $xml->xpath("//PRODUCT/LANG[.='E']/../TITLE"); // 默认返回一个 SimpleXmlElement 数组
$title = (string) $xml->xpath("//PRODUCT/LANG[.='E']/../TITLE")[0]; // 以这种方式仅将值保存为字符串
?>
11
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>Midnight Rain</title>
<genre>Fantasy</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 Developer's Guide
[genre] => Computer
[price] => 44.95
[publish_date] => 2000-10-01
[description] => An in-depth look at creating applications
with XML.
)
...
5
canuemail at gmail dot com
16 年前
如果您想根据一个值从 xml 中搜索多个值,那么此代码可能对您有所帮助。

如果有
<Record>
<country>Pakistan</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;
}

}
?>
3
Anonymous
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. 将字符串注册为默认命名空间,并在所有查询中使用该字符串。不幸的是,空字符串将不起作用。
在 XPath2.0 成为默认库之前,目前不存在其他选项。
1
awoerl at contentserv dot com
7 年前
我尝试在我传递的 xpath 语句中使用字符串函数,例如

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

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

xpath 方法是否可能不支持函数调用?
3
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
1
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']"); ?>
To Top