如何更改DOM中元素的名称?

时间:2009-04-22 06:13:14

标签: php xml dom

在带有DOM的PHP中,我有一个DomElement对象,它表示< identity />元件。

我有一个案例需要更改它,因此其元素名称为< person />,但保留相同的子元素和属性。

更改DomElement的元素名称并保留其子元素和属性的最简单方法是什么?

编辑:我刚刚找到very similar question(虽然没有很好的答案)。

3 个答案:

答案 0 :(得分:13)

您可以使用importNode()<identity>元素的childNodes复制到新创建的<person>元素吗?

function changeName($node, $name) {
    $newnode = $node->ownerDocument->createElement($name);
    foreach ($node->childNodes as $child){
        $child = $node->ownerDocument->importNode($child, true);
        $newnode->appendChild($child, true);
    }
    foreach ($node->attributes as $attrName => $attrNode) {
        $newnode->setAttribute($attrName, $attrNode);
    }
    $newnode->ownerDocument->replaceChild($newnode, $node);
    return $newnode;
}

$domElement = changeName($domElement, 'person');

也许类似的东西可行,或者您可以尝试使用cloneChild()

编辑:实际上,我刚刚意识到原始函数会丢失节点的位置。根据thomasrutter链接的问题,应该使用replaceChild()

答案 1 :(得分:8)

感谢您的帖子,我可以快速为我解决同样的问题。但是,我有一个DOM_NOT_FOUND异常。这可能是一个PHP版本问题,因为原帖是5年。

根据PHP文档(2014年2月)

DOM_NOT_FOUND
  Raised if oldnode is not a child of this node. 

所以,我已经取代了

$newnode->ownerDocument->replaceChild($newnode, $node);

$node->parentNode->replaceChild($newnode, $node);

这是完整的功能(已测试):

public static function changeTagName($node, $name) {
    $childnodes = array();
    foreach ($node->childNodes as $child){
        $childnodes[] = $child;
    }
    $newnode = $node->ownerDocument->createElement($name);
    foreach ($childnodes as $child){
        $child2 = $node->ownerDocument->importNode($child, true);
        $newnode->appendChild($child2);
    }
    foreach ($node->attributes as $attrName => $attrNode) {
        $attrName = $attrNode->nodeName;
        $attrValue = $attrNode->nodeValue;
        $newnode->setAttribute($attrName, $attrValue);
    }
    $node->parentNode->replaceChild($newnode, $node);
    return $newnode;
}

值得一提的是,当你想使用这个函数时,你应该按照相反的顺序遍历DOM树,如其他文章中所述。

更新:在Windows上使用和更新PHP版本5.5.15几个月之后,我遇到错误,说$ attr无法转换为字符串。所以我更新了第三个 - 上面的每个循环。

答案 2 :(得分:1)

注意:我尝试过Calvin的代码,它对我有用但不完全。如果我替换的标签有嵌套标签,有些子标签有时会丢失。

原因是childNodes是节点子节点的 live DOMNodeListappendChild在DOM中移动节点,从而影响列表排序。如果您只是在childNodes上执行foreach,则循环可以跳过一些孩子。

我的解决方案是使用while循环。这样您就不必将任何节点复制到数组中。

我已将所有内容打包在一个方便的函数中,该函数接受一个字符串并返回一个字符串,并且应该使用utf-8。以下是在PHP 5.5.9中测试的。

function renameTags($html, $oldname, $name) {
    $dom = new DOMDocument( '1.0', 'utf-8' );
    $fragment = $dom->createDocumentFragment();
    if ( $fragment->appendXML( $html ) ) {
        $dom->appendChild( $fragment );

        $nodesToAlter = $dom->getElementsByTagName( $oldname );

        while ($nodesToAlter->length) {
            $node = $nodesToAlter->item(0);

            $newnode = $node->ownerDocument->createElement($name);

            while ($node->hasChildNodes()) {
                $child = $node->childNodes->item(0);
                $child = $node->ownerDocument->importNode($child, true);
                $newnode->appendChild($child);
            }
            foreach ($node->attributes as $attr) {
                $attrName = $attr->nodeName;
                $attrValue = $attr->nodeValue;
                $newnode->setAttribute($attrName, $attrValue);
            }
            $newnode->ownerDocument->replaceChild($newnode, $node);

        }
        return $dom->saveHTML();
    } else {
        //error
    }
}


$html = 'Testing <b foo="bar" baz="foo">nested tags in <i lol="cat"> html strings</i></b> and <b>stuff</b>';

echo renameTags($html, 'b', 'strong');

打印:

Testing <strong foo="bar" baz="foo">nested tags in <i lol="cat"> html strings</i></strong> and <strong>stuff</strong>