Как изменить корень узла с помощью методов DomDocument?
Как изменить только имя корневого тега узла DOM?
В модели DOM-Document мы не можем изменить свойство documentElement
из DOMElement
объект, поэтому нам нужно "перестроить" узел... Но как "перестроить" с помощью 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'
5 ответов
Поскольку на этот вопрос еще не получен ответ, ошибка, о которой вы узнаете, не найдена, из-за небольшой ошибки в 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);
ISTM в вашем подходе вы пытаетесь импортировать узлы из другого 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();
Это вариация моего "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 (без ошибок!), Награда за вас!
Надеюсь, я ничего не пропустил, но у меня случилась похожая проблема, и я смог ее решить, используя 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
,
Дайте нам знать, если это сработало для вас. Приветствия.
РЕДАКТИРОВАТЬ: заметил в версии 20031129, что DomDocument::$formatOutput
, если установлено, не форматирует $newRoot
вывод, когда вы, наконец, позвоните $doc->saveXML()