可能是一个愚蠢的问题,但到目前为止,我无法弄清楚这一点......
我有一个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;
没有名称空间的解决方案
它始终与根xmlns="http://www.w3.org/1999/xhtml"
标记中的html
命名空间有关。 //body
会选择任何名称空间 NOT 的正文标记。由于我们确实指定了默认命名空间,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
就像预期一样......
答案 0 :(得分:2)
这段代码对我有用:
$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
我认为两个关键点是:
//body//meta
- xpath必须反映主体和元素元素之间可以有更多元素。因此//
和body
之间的meta
。感谢Dimitri的解释,我现在可以更好地理解我只闻到的名称空间问题,并且可以将代码更新为loadXML()兼容版本(仅修改后的行):
$xml->loadXml($temp);
$xpath = new DOMXPath($xml);
$xpath->registerNamespace('xhtml', 'http://www.w3.org/1999/xhtml');
$path = "//xhtml:body//xhtml:meta";
这会将文档加载为XML。然后,它从文档中注册名称为xhtml
的名称空间URI,用于xpath对象。
然后修改了xpath查询以正确反映元素表达式的命名空间。