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

时间:2022-04-16 04:39:56

In PHP with DOM, I have a DomElement object which represents an <identity/> element.

在PHP中,我有一个DomElement对象,它表示一个 元素。

I have one case where I need to change this so its element name is <person/>, but keep the same child elements and attributes.

我有一种情况,我需要更改它的元素名为 ,但保留相同的子元素和属性。

What would be the simplest way of changing an element name of a DomElement and keeping its children and attributes?

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

Edit: I have just found a very similar question (though it doesn't have very good answers).

编辑:我刚刚发现了一个非常相似的问题(虽然它没有很好的答案)。

3 个解决方案

#1


12  

Could you use importNode() to copy the childNodes of your <identity> element to a newly created <person> element?

可以使用importNode()将 元素的子节点复制到新创建的 元素吗?

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');

Perhaps something like that would work, or you could try using cloneChild().

也许这样的方法可行,或者您可以尝试使用cloneChild()。

Edit: Actually, I just realized that the original function would lose the placement of the node. As per the question thomasrutter linked to, replaceChild() should be used.

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

#2


7  

Thanks to your post, I could quickly solve the same issue for me. However, I had a DOM_NOT_FOUND exception. This is probably a PHP Version issue, since the original post is 5 years old.

由于你的帖子,我可以很快解决同样的问题。但是,我有一个DOM_NOT_FOUND异常。这可能是一个PHP版本的问题,因为最初的帖子已有5年的历史。

According to the PHP Documentation (Feb 2014)

根据PHP文档(2014年2月)

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

So, I have replaced

所以,我已经取代了

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

with

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

Here is the complete function (tested):

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

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;
}

It is also worth mentioning that when you want to use this function, you should traverse the DOM Tree in reversed order as explained in other posts.

值得一提的是,当您想要使用这个函数时,您应该按相反的顺序遍历DOM树。

UPDATE: After months of using and updating to PHP Version 5.5.15 on windows, I had an error saying $attr could not be converted to a string. So I updated third for-each loop above.

更新:在windows上使用和更新PHP 5.5.15版本几个月后,我有一个错误,说$attr不能转换为字符串。所以我更新了上面的第三个for-each循环。

#3


0  

NOTE: I tried Calvin's code and it sort of worked for me but not quite. If the tag I was replacing had nested tags, some child tags would sometimes get lost.

注意:我试过了加尔文的密码,它对我有点效果,但不是很好。如果我替换的标签有嵌套标签,一些子标签有时会丢失。

The reason is that childNodes is a live DOMNodeList of a node's children, and appendChild moves the nodes around in the DOM, thus affecting the list ordering. If you just do foreach on a childNodes the loop can skip some children.

原因是子节点是节点子节点的活动DOMNodeList, appendChild移动DOM中的节点,从而影响列表排序。如果你只是对每个孩子都做,循环可以跳过一些孩子。

My solution was to use a while loop. This way you don't have to copy any nodes to an array.

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

I have packaged everything in a convenient function that takes a string and returns a string and should work with utf-8. The following is tested in PHP 5.5.9.

我已经将所有东西打包到一个方便的函数中,该函数接受一个字符串并返回一个字符串,并且应该使用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');

Prints:

打印:

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

#1


12  

Could you use importNode() to copy the childNodes of your <identity> element to a newly created <person> element?

可以使用importNode()将 元素的子节点复制到新创建的 元素吗?

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');

Perhaps something like that would work, or you could try using cloneChild().

也许这样的方法可行,或者您可以尝试使用cloneChild()。

Edit: Actually, I just realized that the original function would lose the placement of the node. As per the question thomasrutter linked to, replaceChild() should be used.

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

#2


7  

Thanks to your post, I could quickly solve the same issue for me. However, I had a DOM_NOT_FOUND exception. This is probably a PHP Version issue, since the original post is 5 years old.

由于你的帖子,我可以很快解决同样的问题。但是,我有一个DOM_NOT_FOUND异常。这可能是一个PHP版本的问题,因为最初的帖子已有5年的历史。

According to the PHP Documentation (Feb 2014)

根据PHP文档(2014年2月)

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

So, I have replaced

所以,我已经取代了

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

with

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

Here is the complete function (tested):

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

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;
}

It is also worth mentioning that when you want to use this function, you should traverse the DOM Tree in reversed order as explained in other posts.

值得一提的是,当您想要使用这个函数时,您应该按相反的顺序遍历DOM树。

UPDATE: After months of using and updating to PHP Version 5.5.15 on windows, I had an error saying $attr could not be converted to a string. So I updated third for-each loop above.

更新:在windows上使用和更新PHP 5.5.15版本几个月后,我有一个错误,说$attr不能转换为字符串。所以我更新了上面的第三个for-each循环。

#3


0  

NOTE: I tried Calvin's code and it sort of worked for me but not quite. If the tag I was replacing had nested tags, some child tags would sometimes get lost.

注意:我试过了加尔文的密码,它对我有点效果,但不是很好。如果我替换的标签有嵌套标签,一些子标签有时会丢失。

The reason is that childNodes is a live DOMNodeList of a node's children, and appendChild moves the nodes around in the DOM, thus affecting the list ordering. If you just do foreach on a childNodes the loop can skip some children.

原因是子节点是节点子节点的活动DOMNodeList, appendChild移动DOM中的节点,从而影响列表排序。如果你只是对每个孩子都做,循环可以跳过一些孩子。

My solution was to use a while loop. This way you don't have to copy any nodes to an array.

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

I have packaged everything in a convenient function that takes a string and returns a string and should work with utf-8. The following is tested in PHP 5.5.9.

我已经将所有东西打包到一个方便的函数中,该函数接受一个字符串并返回一个字符串,并且应该使用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');

Prints:

打印:

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