如何只更改DOM节点的root标签名称?
在DOM-Document模型中,我们无法更改documentElement
对象的属性DOMElement
,因此,我们需要“重建”节点...但是如何使用{{“重建” 1}}属性?
注意:我可以通过converting to string with saveXML and cuting root by regular expressions执行此操作...但这是一种解决方法,而不是DOM解决方案。
PHP示例(不起作用,但是为什么?):
childNodes
// 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)
来自@can建议(仅指点链接)和我对穷人dom-domdocument-renamenode manual的解释。
Fatal error: Uncaught exception 'DOMException'
with message 'Wrong Document Error'
renameNode()方法导致错误,
function DomElement_renameRoot2($ele,$ROOTAG='newRoot') {
$ele->ownerDocument->renameNode($ele,null,"h1");
return $ele;
}
Warning: DOMDocument::renameNode(): Not yet implemented
replaceChild()方法导致错误,
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;
}
答案 0 :(得分:3)
首先,您需要了解DOMDocument
只是文档树的层次根。它的名字始终是#document
。您想要重命名根元素,即$document->documentElement
。
如果要将文档中的节点复制到另一个文档,则需要使用importNode()函数:$document->importNode($nodeInAnotherDocument)
修改强>
renameNode()
尚未实现,因此您应该创建另一个root,并将其替换为旧的root。如果您使用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);
答案 1 :(得分:2)
由于尚未真正回答,因此您找不到的错误是因为您复制的renameNode()
函数出现了一点错误。
在a somewhat related question about renaming different elements in the DOM中,我也看到了这个问题,used an adoption of that function in my answer没有出现此错误:
/**
* 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手册中的示例一样,它错过了处理命名空间节点的变体。我还没有预订最好的东西,例如创建一个处理它的附加函数,从节点接管命名空间以在不同的函数中显式重命名或更改命名空间。
答案 2 :(得分:1)
这是我的“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(没有错误!), bounty 适合您!
答案 3 :(得分:1)
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();
答案 4 :(得分:0)
我希望我没有遗漏任何东西,但我碰巧遇到了类似的问题,并且能够通过使用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()
输出