Как изменить корень узла с помощью методов DomDocument?

Как изменить только имя тега корня узла DOM?

В модели DOM-Document мы не можем изменить свойство documentElement объекта DOMElement , поэтому нам нужно «перестроить» узел … Но как «перестроить» с childNodes свойства childNodes ?


ПРИМЕЧАНИЕ. Я могу сделать это, преобразовывая в строку с помощью saveXML и вырезая корень с помощью регулярных выражений … Но это обходное решение, а не DOM-решение.


Пробовал, но не работает, примеры PHP

Пример PHP (не работает, но ПОЧЕМУ?):

Try-1

  // DOMElement::documentElement can not be changed, so... function DomElement_renameRoot1($ele,$ROOTAG='newRoot') { if (gettype($ele)=='object' && $ele->nodeType==XML_ELEMENT_NODE) { $doc = new DOMDocument(); $eaux = $doc->createElement($ROOTAG); // DOMElement foreach ($ele->childNodes as $node) if ($node->nodeType == 1) // DOMElement $eaux->appendChild($node); // error! elseif ($node->nodeType == 3) // DOMText $eaux->appendChild($node); // error! return $eaux; } else die("ERROR: invalid DOM object as input"); } 

appendChild($node) вызывает ошибку:

  Fatal error: Uncaught exception 'DOMException' with message 'Wrong Document Error' 

Try-2

Из предложения @can (только указательная ссылка) и моей интерпретации плохого руководства dom-domdocument-renamenode .

  function DomElement_renameRoot2($ele,$ROOTAG='newRoot') { $ele->ownerDocument->renameNode($ele,null,"h1"); return $ele; } 

Метод renameNode () вызвал ошибку,

 Warning: DOMDocument::renameNode(): Not yet implemented 

Try-3

Из руководства по PHP, комментарий 1 .

  function renameNode(DOMElement $node, $newName) { $newNode = $node->ownerDocument->createElement($newName); foreach ($node->attributes as $attribute) $newNode->setAttribute($attribute->nodeName, $attribute->nodeValue); while ($node->firstChild) $newNode->appendChild($node->firstChild); // changes firstChild to next!? $node->ownerDocument->replaceChild($newNode, $node); // changes $node? // not need return $newNode; } 

Метод replaceChild () вызвал ошибку,

 Fatal error: Uncaught exception 'DOMException' with message 'Not Found Error' 

Поскольку на этот вопрос пока еще не ответил, ошибка, о которой вы не нашли, renameNode() с небольшой ошибкой в ​​функции renameNode() вы скопировали.

В несколько смешном вопросе о переименовании разных элементов в DOM я тоже видел эту проблему и использовал принятие этой функции в своем ответе , который не имеет этой ошибки:

 /** * Renames a node in a DOM Document. * * @param DOMElement $node * @param string $name * * @return DOMNode */ function dom_rename_element(DOMElement $node, $name) { $renamed = $node->ownerDocument->createElement($name); foreach ($node->attributes as $attribute) { $renamed->setAttribute($attribute->nodeName, $attribute->nodeValue); } while ($node->firstChild) { $renamed->appendChild($node->firstChild); } return $node->parentNode->replaceChild($renamed, $node); } 

Возможно, вы заметили это в последней строке тела функции: это используется ->parentNode вместо ->ownerDocument . Поскольку $node не был дочерним элементом документа, вы получили ошибку. И также было неправильно предполагать, что это должно быть. Вместо этого используйте родительский элемент для поиска там ребенка, чтобы его заменить;)

Это пока не указано в справочных руководствах PHP, если вы перешли по ссылке на сообщение в блоге, в котором первоначально предлагалась renameNode() вы могли бы найти комментарий ниже, предлагая это решение.

Во всяком случае, мой вариант здесь использует немного другое имя переменной и более отчетливо относится к типам. Как пример в руководстве PHP, он пропускает вариант, который имеет дело с узлами пространства имен. Я еще не забронировал то, что было бы лучше, например, создать дополнительную функцию, связанную с ним, перехватить пространство имен из узла, чтобы переименовать или изменить пространство имен явно в другой функции.

Во-первых, вам нужно понять, что DOMDocument является только иерархическим корнем дерева документов. Это имя всегда # #document . Вы хотите переименовать корневой элемент, который является $document->documentElement .

Если вы хотите скопировать узлы из документа в другой документ, вам нужно будет использовать функцию importNode () : $document->importNode($nodeInAnotherDocument)

Редактировать:

renameNode() еще не реализован, поэтому вы должны сделать еще один корень и просто заменить его на старый. Если вы используете DOMDocument->createElement() вам больше не нужно использовать importNode() .

 $oldRoot = $doc->documentElement; $newRoot = $doc->createElement('new-root'); foreach ($oldRoot->attributes as $attr) { $newRoot->setAttribute($attr->nodeName, $attr->nodeValue); } while ($oldRoot->firstChild) { $newRoot->appendChild($oldRoot->firstChild); } $doc->replaceChild($newRoot, $oldRoot); 

Это вариант моего «Try-3» (см. Вопрос), и он отлично работает !

  function xml_renameNode(DOMElement $node, $newName, $cpAttr=true) { $newNode = $node->ownerDocument->createElement($newName); if ($cpAttr && is_array($cpAttr)) { foreach ($cpAttr as $k=>$v) $newNode->setAttribute($k, $v); } elseif ($cpAttr) foreach ($node->attributes as $attribute) $newNode->setAttribute($attribute->nodeName, $attribute->nodeValue); while ($node->firstChild) $newNode->appendChild($node->firstChild); return $newNode; } 

Конечно, если вы покажете, как использовать DOMDocument :: renameNode (без ошибок!), Щедрость идет за вами!

ISTM в вашем подходе вы DOMDocument импортировать узлы из другого DOMDocument , поэтому вам нужно использовать метод importNode() :

 $d = new DOMDocument(); /* Make a `foo` element the root element of $d */ $root = $d->createElement("foo"); $d->appendChild($root); /* Append a `bar` element as the child element of the root of $d */ $child = $d->createElement("bar"); $root->appendChild($child); /* New document */ $d2 = new DOMDocument(); /* Make a `baz` element the root element of $d2 */ $root2 = $d2->createElement("baz"); $d2->appendChild($root2); /* * Import a clone of $child (from $d) into $d2, * with its child nodes imported recursively */ $child2 = $d2->importNode($child, true); /* Add the clone as the child node of the root of $d2 */ $root2->appendChild($child2); 

Тем не менее, гораздо проще добавить дочерние узлы к новому родительскому элементу (тем самым перемещая их) и заменить старый корень на этот родительский элемент:

 $d = new DOMDocument(); /* Make a `foo` element the root element of $d */ $root = $d->createElement("foo"); $d->appendChild($root); /* Append a `bar` element as the child element of the root of $d */ $child = $d->createElement("bar"); $root->appendChild($child); /* <?xml version="1.0"?> <foo><bar/></foo> */ echo $d->saveXML(); $root2 = $d->createElement("baz"); /* Make the `bar` element the child element of `baz` */ $root2->appendChild($child); /* Replace `foo` with `baz` */ $d->replaceChild($root2, $root); /* <?xml version="1.0"?> <baz><bar/></baz> */ echo $d->saveXML(); 

Надеюсь, что я ничего не пропустил, но у меня возникла аналогичная проблема, и я смог ее решить, используя DomDocument::replaceChild(...) .

  /* @var $doc DOMDocument */ $doc = DOMImplementation::createDocument(NULL, 'oldRoot'); /* @var $newRoot DomElement */ $newRoot = $doc->createElement('newRoot'); /* all the code to create the elements under $newRoot */ $doc->replaceChild($newRoot, $doc->documentElement); $doc->documentElement->isSameNode($newRoot) === true; 

Сначала меня отбросило то, что документ $doc->documentElement был только что прочитан, но вышеописанное и кажется гораздо более простым решением, если $newRoot был создан с тем же DomDocument , иначе вам понадобится решение importNode как описано выше. Из вашего вопроса видно, что $newRoot может быть создан из того же $doc .

Сообщите нам, если бы это сработало для вас. Приветствия.

EDIT: замечено в версии 20031129, что параметр DomDocument::$formatOutput , если он установлен, не форматирует вывод $newRoot когда вы, наконец, вызываете $doc->saveXML()