如何使用DomDocument方法更改节点的根?

时间:2013-03-26 20:28:52

标签: php xml domdocument appendchild removechild

如何只更改DOM节点的root标签名称?

在DOM-Document模型中,我们无法更改documentElement对象的属性DOMElement,因此,我们需要“重建”节点...但是如何使用{{“重建” 1}}属性?


注意:我可以通过converting to string with saveXML and cuting root by regular expressions执行此操作...但这是一种解决方法,而不是DOM解决方案。


尝试但不起作用,PHP示例

PHP示例(不起作用,但是为什么?):

尝试-1

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)

尝试-2

来自@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;
 }

尝试-3

来自PHP manual, comment 1

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

5 个答案:

答案 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()输出