假设您有一个带有嵌套标签的 DOM 树,我想通过删除重复项来清理 DOM 对象。但是,这只适用于标签只有一个子标签的情况同一类型。例如,
修复 <div><div>1</div></div>
而不是 <div><div>1</div><div>2</div></div>
.
我正在尝试弄清楚如何使用 PHP's DOM extension 来做到这一点.下面是起始代码,我正在寻求帮助来确定所需的逻辑。
<?php
libxml_use_internal_errors(TRUE);
$html = '<div><div><div><p>Some text here</p></div></div></div>';
$dom = new DOMDocument;
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadHTML($html);
function dom_remove_duplicate_nodes($node)
{
var_dump($node);
if($node->hasChildNodes())
{
for($i = 0; $i < $node->childNodes->length; $i++)
{
$child = $node->childNodes->item($i);
dom_remove_duplicate_nodes($child);
}
}
else
{
// Process here?
}
}
dom_remove_duplicate_nodes($dom);
我收集了一些帮助函数,这些函数可以使像 JavaScript 一样更容易地处理 DOM 节点。
function DOM_delete_node($node)
{
DOM_delete_children($node);
return $node->parentNode->removeChild($node);
}
function DOM_delete_children($node)
{
while (isset($node->firstChild))
{
DOM_delete_children($node->firstChild);
$node->removeChild($node->firstChild);
}
}
function DOM_dump_child_nodes($node)
{
$output = '';
$owner_document = $node->ownerDocument;
foreach ($node->childNodes as $el)
{
$output .= $owner_document->saveHTML($el);
}
return $output;
}
function DOM_dump_node($node)
{
if($node->ownerDocument)
{
return $node->ownerDocument->saveHTML($node);
}
}
最佳答案
您可以使用 DOMDocument
和 DOMXPath
轻松完成此操作. XPath 在您的情况下尤其有用,因为您可以轻松划分逻辑以选择要删除的元素以及删除元素的方式。
首先,规范化输入。我不完全清楚你对空空格的意思,我认为它可能是空文本节点(可能已被删除,因为 preserveWhiteSpace
是 FALSE
但我不确定)或者它们是否规范化空白为空。我选择了第一个(如果有必要的话),以防它是另一个变体,我留下了评论使用什么来代替:
$xp = new DOMXPath($dom);
//remove empty textnodes - if necessary at all
// (in case remove WS: [normalize-space()=""])
foreach($xp->query('//text()[""]') as $i => $tn)
{
$tn->parentNode->removeChild($tn);
}
在此 textnode 规范化之后,您应该不会遇到您在此处评论中提到的问题。
下一部分是找到所有与其父元素同名并且是唯一子元素的元素。这又可以用xpath来表示。如果找到这样的元素,则将其所有子元素移动到父元素,然后该元素也将被删除:
// all child elements with same name as parent element and being
// the only child element.
$r = $xp->query('body//*/child::*[name(.)=name(..) and count(../child::*)=1]');
foreach($r as $i => $dupe)
{
while($dupe->childNodes->length)
{
$child = $dupe->firstChild;
$dupe->removeChild($child);
$dupe->parentNode->appendChild($child);
}
$dupe->parentNode->removeChild($dupe);
}
如您在演示中所见,这独立于文本节点和注释。如果你不想要那个,例如在实际文本中,计算子项的表达式需要覆盖所有节点类型。但我不知道这是否是您的确切需求。如果是,则计算所有节点类型的子节点数:
body//*/child::*[name(.)=name(..) and count(../child::node())=1]
如果您没有预先规范化空文本节点(删除空文本节点),那么这就太严格了。选择你需要的工具集,我认为规范化加上这个严格的规则可能是最好的选择。
关于php - 如何在 PHP 中删除重复的、嵌套的 DOM 元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7972199/