可能是一个愚蠢的问题,但到目前为止我无法弄清楚......
我有一个 XHTML 文档作为字符串。它在$temp
到目前为止一切顺利。我想做两件事。我想选择正文中的所有元标记(它们之所以存在,是因为它们与微数据结合使用(,然后删除它们。删除微数据属性后。
$xml=new DOMDocument();
$xml->loadXML($temp);
$xpath = new DOMXPath($xml);
$attr = $xpath->query("//@itemscope|//@itemprop|//@itemtype|//@itemid|//@itemref");
foreach ($attr as $entry)
$entry->parentNode->removeAttribute($entry->nodeName);
这行得通。但是我无法使用 Xpath 选择任何节点。
$xpath = new DOMXPath($xml); // thought I had to update this after changing the XML
echo $xpath->query("//body")->length; // => 0
echo $xml->getElementsByTagName("body")->length; // => 1
所以问题1:如何使用Xpath选择节点。为什么这不起作用?
不过,这可以获取节点列表:
$node = $xml->getElementsByTagName("body")->item(0)->getElementsByTagName("meta");
我想删除我将使用它的节点:(类似于删除上面的属性(
foreach ($node as $entry)
{
$entry->parentNode->removeChild($entry);
}
但节点仍然存在。
所以有问题2:如何从XML文件中删除节点。
特别是任何身体节点中任意位置的元节点。
谢谢。
更新
让我添加一个 HTML 测试用例:
$temp='<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<meta charset="utf-8"/>
</head>
<body id="dok" itemscope="itemscope" itemtype="http://schema.org/WebPage" >
<div><div><div><meta itemprop="dummy" content="something"/></div></div></div>
<span><meta itemprop="dummy2" content="something2"/></span>
</body>
</html>';
通过上述内容,xPath 尝试选择正文给我的长度为 0,我无法从正文中删除所有元标记......
更新
这适用于 loadXML(( 方法:
$xpath = new DOMXPath($xml);
$xpath->registerNamespace("x","http://www.w3.org/1999/xhtml");
echo $xpath->query("//x:body")->length;
没有命名空间的解决方案
它始终是关于根html
标签中的xmlns="http://www.w3.org/1999/xhtml"
命名空间。 //body
选择不属于任何命名空间的任何正文标记。由于我们确实指定了一个默认命名空间,并且body
是该命名空间的一部分,因此//body
不会选择它。我不知道以什么名称访问已经是XHTML固有的命名空间,而无需以名称声明它,但是如果我们在创建XML之前将其剥离,一切都很好。完成后,我们可以将其重新添加。
$temp = str_replace('xmlns="http://www.w3.org/1999/xhtml"','',$temp);
$xml=new DOMDocument();
$xml->loadXML($temp);
$xpath = new DOMXPath($xml);
$attr = $xpath->query("//@itemscope|//@itemprop|//@itemtype|//@itemid|//@itemref");
foreach ($attr as $entry)
$entry->parentNode->removeAttribute($entry->nodeName);
$node = $xpath->query("//body//meta");
foreach ($node as $entry)
{
$entry->parentNode->removeChild($entry);
}
$temp=$xml->saveXML();
$temp = str_replace('<html','<html xmlns="http://www.w3.org/1999/xhtml"',$temp);
这样//body//meta
就可以按预期工作...
这段代码为我完成了这项工作:
$temp='<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<meta charset="utf-8"/>
</head>
<body id="dok" itemscope="itemscope" itemtype="http://schema.org/WebPage" >
<div><div><div><meta itemprop="dummy" content="something"/></div></div></div>
<span><meta itemprop="dummy2" content="something2"/></span>
</body>
</html>';
$xml=new DOMDocument();
$xml->loadHtml($temp);
$xpath = new DOMXPath($xml); // thought I had to update this after changing the XML
$path = "//body//meta";
echo $xpath->query($path)->length, "'n"; # 2
foreach ($xpath->query($path) as $entry)
{
$entry->parentNode->removeChild($entry);
}
echo $xpath->query($path)->length, "'n"; # 0
我认为两个关键点是:
- 将文档加载为 HTML - 我无法正确解释它,但我认为 XML 正在引入命名空间,这些应该反映在 xpath 中。但是我对命名空间不太熟悉,无法真正解释它。但是,加载为 HTML 会使查询"按预期">工作,这在技术上不是正确的期望。
-
//body//meta
- xpath必须反映主体和元元素之间可以有更多的元素。因此,body
和meta
之间的//
。
命名空间和 XML
多亏了 Dimitri 的解释,我现在可以更好地理解我只闻到的命名空间问题,并且可以将代码更新为 loadXML(( 兼容版本(仅限修改后的行(:
$xml->loadXml($temp);
$xpath = new DOMXPath($xml);
$xpath->registerNamespace('xhtml', 'http://www.w3.org/1999/xhtml');
$path = "//xhtml:body//xhtml:meta";
这会将文档加载为 XML。然后,它使用名称 xpath 对象的 xhtml
从文档中注册命名空间 URI。
然后修改 xpath 查询以正确反映元素表达式的命名空间。