我的 DOMDocument 中有鬼?
ghosts in my DOMDocument?
尝试使用以前 运行 的简单 xpath,现在只显示空节点。
来源:任何 XML 文件。假设
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" lang="pt-br" xml:lang="pt-br">
<head> <meta charset="utf-8"/><title>test</title> </head>
<body>
<article id="etc"><p>Hello</p><p>Bye</p></article>
</body>
</html>
全部重做,这里附上完整的测试:
$dom2 = new DOMDocument;
$dom2->Load($pathFile);
$xpath2 = new DOMXPath($dom2);
$entries = $xpath->query('//p');
// nothing here, all empty:
var_dump($entries); // zero!
foreach ($entries as $entry) {
echo "Found {$entry->nodeValue},";
}
// by all here!
foreach($dom2->getElementsByTagName('*') as $e )
print "\n name={$e->nodeName}"; // all tags!
有什么问题,为什么 xpath 不是 运行?
那是因为您的 xml 定义了默认命名空间:
xmlns="http://www.w3.org/1999/xhtml"
因此您需要注册一个命名空间,然后使用命名空间标签名称进行搜索:
$xpath->registerNamespace('x', 'http://www.w3.org/1999/xhtml');
$entries = $xpath->query('//x:p');
这是 W3C 的 DomDocument v1.0 标准的一个老问题。 an old site commented 关于 XPath 初学者的惊喜,
One of the commonly asked questions about (...) is:
"Why nothing matched for my XPath expression which seems right to me?"
Common cause of these problems is not properly defining a namespace for XPath.
但是初学者是对的,丑陋的行为 "default thing"...所以让我们保持初学者对什么是简单而好的直觉。
看到一个似乎不是您需要的 XPath 真是太可怕了(当它的标签没有前缀时 XML 看起来是什么)。标签是简单的标签,需要简单的 XPath。
可靠的解决方法
使用最佳解决方案修复丑陋的 XPath 查询行为。这不是微不足道的,因为 root 的 xmlns
属性 is read-only,所以我们需要通过新字符串 XML:
重新做 DOM 对象
$expTag = 'html'; // config expected tag-root
$expNs = 'http://www.w3.org/1999/xhtml'; // config
// ...
$e = $dom->documentElement; // root node
// Validate input (as expecteds configs) and change tag root:
if ($e->nodeName==$expTag && $e->hasAttribute('xmlns')
&& $e->getAttribute('xmlns')==$expNs) {
// can't do $e->removeAttribute('xmlns') because is read-only!
$xml = $dom->C14N(); // normalize quotes and remove repeateds
$xml = preg_replace("#^<$expTag (.*?)xmlns=\"[^\"]+\"#", "<$expTag$1", $xml);
$dom = DOMDocument::LoadXML($xml);
} else
die("\n ERROR: something not expected.\n");
//...
$xpath = new DOMXPath($dom);
$entries = $xpath->query('//p'); // perfect, now back simple to express XPath!
仅当您没有限制时才必须使用此解决方案,例如在 digital preservation 上下文中。
其他实际情况下的问题是 save/reload 完整 XML 作为字符串的高成本 (CPU),为了安全起见,成本更高 C14N 方法,为正则表达式准备安全 XML。
使用 C14N(对于 数字保存 上下文中的其他内容也有好处)对于确保正则表达式的正确行为是必要的——严格来说 getAttribute()
方法可能会受到属性重复的影响,但是我们可以忽略这种"second order"影响,或者将检查转移到正则表达式。
尝试使用以前 运行 的简单 xpath,现在只显示空节点。
来源:任何 XML 文件。假设
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" lang="pt-br" xml:lang="pt-br">
<head> <meta charset="utf-8"/><title>test</title> </head>
<body>
<article id="etc"><p>Hello</p><p>Bye</p></article>
</body>
</html>
全部重做,这里附上完整的测试:
$dom2 = new DOMDocument;
$dom2->Load($pathFile);
$xpath2 = new DOMXPath($dom2);
$entries = $xpath->query('//p');
// nothing here, all empty:
var_dump($entries); // zero!
foreach ($entries as $entry) {
echo "Found {$entry->nodeValue},";
}
// by all here!
foreach($dom2->getElementsByTagName('*') as $e )
print "\n name={$e->nodeName}"; // all tags!
有什么问题,为什么 xpath 不是 运行?
那是因为您的 xml 定义了默认命名空间:
xmlns="http://www.w3.org/1999/xhtml"
因此您需要注册一个命名空间,然后使用命名空间标签名称进行搜索:
$xpath->registerNamespace('x', 'http://www.w3.org/1999/xhtml');
$entries = $xpath->query('//x:p');
这是 W3C 的 DomDocument v1.0 标准的一个老问题。 an old site commented 关于 XPath 初学者的惊喜,
One of the commonly asked questions about (...) is:
"Why nothing matched for my XPath expression which seems right to me?"
Common cause of these problems is not properly defining a namespace for XPath.
但是初学者是对的,丑陋的行为 "default thing"...所以让我们保持初学者对什么是简单而好的直觉。
看到一个似乎不是您需要的 XPath 真是太可怕了(当它的标签没有前缀时 XML 看起来是什么)。标签是简单的标签,需要简单的 XPath。
可靠的解决方法
使用最佳解决方案修复丑陋的 XPath 查询行为。这不是微不足道的,因为 root 的 xmlns
属性 is read-only,所以我们需要通过新字符串 XML:
$expTag = 'html'; // config expected tag-root
$expNs = 'http://www.w3.org/1999/xhtml'; // config
// ...
$e = $dom->documentElement; // root node
// Validate input (as expecteds configs) and change tag root:
if ($e->nodeName==$expTag && $e->hasAttribute('xmlns')
&& $e->getAttribute('xmlns')==$expNs) {
// can't do $e->removeAttribute('xmlns') because is read-only!
$xml = $dom->C14N(); // normalize quotes and remove repeateds
$xml = preg_replace("#^<$expTag (.*?)xmlns=\"[^\"]+\"#", "<$expTag$1", $xml);
$dom = DOMDocument::LoadXML($xml);
} else
die("\n ERROR: something not expected.\n");
//...
$xpath = new DOMXPath($dom);
$entries = $xpath->query('//p'); // perfect, now back simple to express XPath!
仅当您没有限制时才必须使用此解决方案,例如在 digital preservation 上下文中。
其他实际情况下的问题是 save/reload 完整 XML 作为字符串的高成本 (CPU),为了安全起见,成本更高 C14N 方法,为正则表达式准备安全 XML。
使用 C14N(对于 数字保存 上下文中的其他内容也有好处)对于确保正则表达式的正确行为是必要的——严格来说 getAttribute()
方法可能会受到属性重复的影响,但是我们可以忽略这种"second order"影响,或者将检查转移到正则表达式。