使用DOMXPath清理已弃用的HTML代码(将嵌套的<div>标签转换为<p>标签)

时间:2019-05-05 10:13:18

标签: php html domxpath

我正在尝试将存储在旧的MS Access数据库中的RTF文本读取到新的PHP Web应用程序中。清理后的数据将使用CKEditor显示给用户,这对于解析符合标准的HTML代码非常严格。但是,存储在MS Access中的数据通常格式不正确或使用不推荐使用的HTML代码。

以下是我要清理的示例数据:

<div align="right">Previous claim $ &nbsp;&nbsp;935.00<div align="right">&nbsp;&nbsp;This claim $1,572.50</div></div>

该数据本来是两行右对齐的文本,但是MS Access使用了已弃用的 align 属性来设置<div>标签的样式,而不是样式属性,并且在这种情况下它们应该是顺序的时,已经错误地嵌套了它们。

要将示例数据转换为两行均右对齐且CKEditor将按预期读取和显示的文本行(即文本显示为右对齐),我尝试将<div>标记替换为<p>标签,然后插入带有正确text-align的内联样式属性以替换不推荐使用的align属性。

我正在使用PHP的DOMXPath清理数据,其代码如下:

$dom = new DOMDocument();
$dom->loadHTML($dataForCleaning, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

$xpath = new DOMXPath($dom);

foreach ($xpath->query('//div[@align]') as $node) {
    $alignment = $node->getAttribute('align');

    $newNode = $dom->createElement('p');
    $newNode->setAttribute("style", "text-align:".$alignment);
    $node->parentNode->insertBefore($newNode, $node);

    foreach ($node->childNodes as $child) {
        $newNode->appendChild($child);
    }

    $node->parentNode->removeChild($node);
}

我使用insertBefore代替appendChild来尝试保持元素序列相同,但这就是导致此嵌套数据示例中出现问题的原因。

对于非嵌套<div>标记作为要清除的输入数据,已清理的输出html是正确的。但是,在这个嵌套的<div>示例中,输出最终为:

<p style="text-align:right">Previous claim $ &nbsp;&nbsp;935.00</p>

请注意,第二行文本(此声明... )已被删除,因为它位于嵌套<div>中,作为父级<div>的子级

我不介意生成的<p>标签是否仍然嵌套,因为CKEditor最终清理了这些标签,但是我需要确保我不会像当前代码那样丢失数据。

在此先感谢您的帮助和指导。 -马克

1 个答案:

答案 0 :(得分:0)

我改变了几件事。首先,我得到的不仅仅是克隆现有节点,而是克隆节点并添加副本(在$newNode->appendChild($child->cloneNode(true));中),第二件事是在移动封闭的节点时,我认为XPath不再指向此移动的节点。因此,除此以外,我会在复制子节点时检查您是否具有与<div align="right">节点相同的模式,如果有,我会以新格式创建一个新节点并添加该新节点...

foreach ($xpath->query('//div[@align]') as $node) {
    $alignment = $node->getAttribute('align');

    $newNode = $dom->createElement('p');
    $newNode->setAttribute("style", "text-align:".$alignment);

    $node->parentNode->insertBefore($newNode, $node);
    foreach ($node->childNodes as $child) {
        if ( $child instanceof DOMElement && $child->localName == "div"
                && $child->attributes->getNamedItem("align")->nodeValue == "right" )    {
            $subNode = $dom->createElement('p', $child->nodeValue );
            $subNode->setAttribute("style", "text-align:".$alignment);
            $newNode->appendChild($subNode);
        }
        else    {
            $newNode->appendChild($child->cloneNode(true));
        }
    }

    $node->parentNode->removeChild($node);
}

您给出的样本将输出...

<p style="text-align:right">
    Previous claim $ &nbsp;&nbsp;935.00
    <p style="text-align:right">&nbsp;&nbsp;This claim $1,572.50</p>
</p>