SimpleXML中的XPath,用于默认名称空间,无需前缀

mpd*_*dio 9 php xml xpath namespaces simplexml

我有一个XML文档,它附加了一个默认命名空间,例如

<foo xmlns="http://www.example.com/ns/1.0">
...
</foo>
Run Code Online (Sandbox Code Playgroud)

实际上,这是一个符合复杂模式的复杂XML文档.我的工作是从中解析出一些数据.为了帮助我,我有一个XPath的电子表格.XPath是相当深层嵌套的,例如

level1/level2/level3[@foo="bar"]/level4[@foo="bar"]/level5/level6[2]
Run Code Online (Sandbox Code Playgroud)

生成XPath的人是模式的专家,所以我假设我不能简化它,或者使用对象遍历快捷方式.

我正在使用SimpleXML来解析所有内容.我的问题与如何处理默认命名空间有关.

由于根元素上有一个默认命名空间,我不能这样做

$xml = simplexml_load_file($somepath);
$node = $xml->xpath('level1/level2/level3[@foo="bar"]/level4[@foo="bar"]/level5/level6[2]');
Run Code Online (Sandbox Code Playgroud)

我必须注册命名空间,将其分配给前缀,然后在我的XPath中使用前缀,例如

$xml = simplexml_load_file($somepath);
$xml->registerXPathNamespace('myns', 'http://www.example.com/ns/1.0');
$node = $xml->xpath('myns:level1/myns:level2/myns:level3[@foo="bar"]/myns:level4[@foo="bar"]/myns:level5/myns:level6[2]');
Run Code Online (Sandbox Code Playgroud)

从长远来看,添加前缀不会是可管理的.

是否有一种正确的方法来处理默认名称空间而无需使用XPath前缀?

使用空前缀不起作用($xml->registerXPathNamespace('', 'http://www.example.com/ns/1.0');).我可以指出默认的命名空间,例如

$xml = file_get_contents($somepath);
$xml = str_replace('xmlns="http://www.example.com/ns/1.0"', '', $xml);
$xml = simplexml_load_string($xml);
Run Code Online (Sandbox Code Playgroud)

但这是在绕过这个问题.

IMS*_*SoP 12

从在线阅读,这不仅限于任何特定的PHP或其他库,而是限于XPath本身 - 至少在XPath版本1.0中

XPath 1.0不包含任何"默认"命名空间的概念,因此无论元素名称如何出现在XML源中,如果它们具有绑定到它们的命名空间,则它们的选择器必须以表单的基本XPath选择器为前缀ns:name.请注意,这ns是在XPath处理器中定义的前缀,而不是正在处理的文档,因此xmlns与XML表示中的属性使用方式无关.

参见例如这个"常见的XSLT错误"页面,讨论密切相关的XSLT 1.0:

要在XPath中访问命名空间元素,必须为其命名空间定义前缀.[...]不幸的是,XSLT 1.0版没有类似于默认命名空间的概念; 因此,您必须一次又一次地重复名称空间前缀.

根据对类似问题的回答,XPath 2.0 确实包含"默认命名空间"的概念,上面链接的XSLT页面也在XSLT 2.0的上下文中提到了这一点.

不幸的是,PHP中的所有内置XML扩展都是建立在libxml2libxslt库的基础之上的,它们只支持XPath和XSLT的1.0版本.

因此,除了预处理文档而不使用命名空间之外,您唯一的选择是找到可以插入PHP的XPath 2.0处理器.

(顺便说一句,值得注意的是,如果你的XML文档中有无前缀的属性,它们在技术上不属于默认命名空间,而是根本没有命名空间;请参阅XML命名空间和未加前缀的属性以讨论命名空间的这种奇怪之处规范.)