使用XPATH和PHP DOM选择和删除节点时出现的问题

使用XPATH和PHP DOM选择和删除节点时出现的问题,php,xpath,domdocument,Php,Xpath,Domdocument,可能是个愚蠢的问题,但到目前为止我还没弄明白 我有一个XHTML文档作为字符串。它在$temp中,到目前为止还不错。我想做两件事。我想选择正文中的所有元标记(它们之所以存在是因为它们与微数据一起使用),然后删除它们。删除已删除的微数据属性后 $xml=new DOMDocument(); $xml->loadXML($temp); $xpath = new DOMXPath($xml); $attr = $xpath->query("//@itemsc

可能是个愚蠢的问题,但到目前为止我还没弄明白

我有一个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");
我想用以下方法删除节点:(类似于删除上面的属性)

但节点仍然存在

因此,问题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>';
不带名称空间的解决方案

这是关于
xmlns=”http://www.w3.org/1999/xhtml“
名称空间始终位于根
html
标记中
//body
选择任何不属于任何命名空间的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);
$temp=str\u replace('xmlns='http://www.w3.org/1999/xhtml“,”,“$temp);
$xml=newdomdocument();
$xml->loadXML($temp);
$xpath=newdomxpath($xml);
$attr=$xpath->query(“/@itemscope |/@itemprop |/@itemtype |//@itemid |/@itemref”);
foreach($attr作为$entry)
$entry->parentNode->removeAttribute($entry->nodeName);
$node=$xpath->query(“//body//meta”);
foreach($node作为$entry)
{
$entry->parentNode->removeChild($entry);
}   
$temp=$xml->saveXML();

$temp=str_replace(“这段代码为我完成了这项工作:

$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
这将以XML的形式加载文档,然后从文档中为xpath对象注册名称为
xhtml
的名称空间URI


然后修改xpath查询以正确反映元素表达式的名称空间。

让我猜猜:您有一个XHTML文档?@Tomalak是的,我有一个XHTML文档作为字符串,但输入错误严重:-),因此我修复了它。您可以试着使用
$xml->loadHTML($temp)
,它甚至可以很好地加载格式错误的内容。Q1A:应该有效
//body
在我看来不错,您可以尝试
//head/meta
以及通过xpath直接访问meta元素。@hakre XHTML作为应用程序/xml使用,格式没有错误。loadXML应该可以
//body
对我不起作用,元标记在body中。。。我用一个虚拟的
$temp
更新了这个问题<代码>echo$xpath->query(“//head”)->长度不适用于示例,但
echo$xpath->query(“/@charset”)->长度工作正常。这是XPath最大的常见问题:XHTML元素位于XHTML名称空间(通常用作默认名称空间)中,但在XPath表达式中,您尝试使用“无名称空间”中的名称访问元素。您需要注册绑定了前缀(比如“x:”)的XHTML名称空间。使用:
x:body
而不是
body
@hakre-Ah我看到我们同时解决了这个问题。没有明确的工作空间,任何事情都无法工作。。。我不知道。问题是,如果XHTML文档没有
xmlns=”http://www.w3.org/1999/xhtml“
在它的根
html
元素中,则不需要这样做。因此,如果输入文档发生更改,仍然使用
loadHtml()可能更安全。我想知道如何将一个名称空间注册为默认名称空间。@hakre它确实有
xmlns=”http://www.w3.org/1999/xhtml“
在它的根
html
元素中,它总是这样做。。。即使是上面的虚拟
$temp
html也拥有它…@hakre它是另一种方式!不是缺少
xmlns=”http://www.w3.org/1999/xhtml“
但它的存在首先是导致问题的原因。。。请参见我对问题的补充,即如何不使用名称空间和纯
//body//meta
@C.O。您误解了我,这是假设性的:如果它不包含名称空间,那么所有元素都在默认名称空间中。你甚至很快就采纳了:正如我所写的,如果文档不包含这个字符串。您可以将其从文档中删除以实现此目的。瞧。这不是问题,而是xpath的工作方式。如果文档包含名称空间,则需要使用xpath注册它。如果删除了该名称空间(就像现在一样),那么默认名称空间将应用于文档中所有没有名称空间的元素,并且不需要为xpath注册名称空间。
    $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);
$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
$xml->loadXml($temp);
$xpath = new DOMXPath($xml);
$xpath->registerNamespace('xhtml', 'http://www.w3.org/1999/xhtml');
$path = "//xhtml:body//xhtml:meta";