Как изменить имя элемента в DOM?

В PHP с DOM у меня есть объект DomElement, который представляет элемент <identity />.

У меня есть один случай, когда мне нужно изменить это, поэтому его имя элемента <person />, но сохраняйте те же дочерние элементы и атрибуты.

Каким будет самый простой способ изменить имя элемента DomElement и сохранить его дочерние элементы и атрибуты?

Редактировать: Я только что нашел очень похожий вопрос (хотя у него нет очень хороших ответов).

Не могли бы вы использовать importNode() для копирования дочерних узлов вашего элемента <identity> на вновь созданный элемент <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() .

Изменить: На самом деле, я просто понял, что исходная функция потеряет место размещения узла. В соответствии с вопросом, с которым связан replaceChild() следует использовать replaceChild() .

Благодаря вашему сообщению, я мог бы быстро решить ту же проблему для меня. Однако у меня было исключение DOM_NOT_FOUND. Вероятно, это проблема с версией PHP, поскольку исходному сообщению 5 лет.

Согласно документации PHP (февраль 2014 г.)

 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 = $attr->nodeName; $attrValue = $attr->nodeValue; $newnode->setAttribute($attrName, $attrValue); } $node->parentNode->replaceChild($newnode, $node); return $newnode; } 

Также стоит упомянуть, что, когда вы хотите использовать эту функцию, вы должны пересечь DOM Tree в обратном порядке, как описано в других сообщениях.

ОБНОВЛЕНИЕ: после нескольких месяцев использования и обновления до версии PHP 5.5.15 на Windows у меня возникла ошибка, указывающая, что $ attr не может быть преобразован в строку. Поэтому я обновил третий для каждого цикла выше.

ПРИМЕЧАНИЕ. Я попробовал код Кальвина, и он работал для меня, но не совсем. Если тег, который я заменил, имел вложенные теги, некоторые дочерние теги иногда терялись.

Причина в том, что childNodes – это живой DOMNodeList дочерних узлов, а appendChild перемещает узлы в DOM, что влияет на упорядочение списка. Если вы просто делаете foreach на childNodes, цикл может пропустить некоторых детей.

Моим решением было использовать цикл 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>