1

如何仅更改 DOM 节点的根标签名称?

在 DOM-Document 模型中,我们不能改变对象的属性documentElementDOMElement所以,我们需要“重建”节点……但是如何用childNodes属性“重建”呢?


注意:我可以通过使用 saveXML 转换为字符串并通过正则表达式切割根来做到这一点......但这是一种解决方法,而不是 DOM 解决方案。


试过但不起作用,PHP 示例

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

试一试

 // 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'

尝试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

试一试 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'
4

5 回答 5

3

首先,您需要了解它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); 
于 2013-04-04T20:15:20.730 回答
3

由于尚未真正回答此问题,因此您遇到的错误 not found 是因为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);
}

你可能已经在函数体的最后一行发现了它:这是 using->parentNode而不是->ownerDocument. 由于$node不是文档的孩子,您确实收到了错误。假设它应该是错误的也是错误的。而是使用父元素在其中搜索子元素以替换它;)

到目前为止,PHP 手册用户注释中尚未对此进行概述,但是,如果您确实点击了最初建议该renameNode()功能的博客文章的链接,您也可以在其下方找到提供此解决方案的评论。

无论如何,我的变体在这里使用了稍微不同的变量命名,并且在类型上更加不同。就像 PHP 手册中的示例一样,它错过了处理命名空间节点的变体。我还没有预订什么是最好的,例如创建一个处理它的附加函数,从节点接管命名空间以重命名或在不同的函数中显式更改命名空间。

于 2013-05-01T08:32:27.250 回答
1

ISTM 在您的方法中尝试从另一个导入节点,因此您需要使用以下方法: DOMDocumentimportNode()

$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();
于 2013-04-04T20:02:51.623 回答
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(没有错误!),那么您将获得赏金

于 2013-03-29T11:26:35.383 回答
0

我希望我没有遗漏任何东西,但我碰巧遇到了类似的问题,并且能够通过使用解决它 use 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上述方法似乎是更简单的解决方案,否则您需要按照上述方法执行解决方案。从您的问题看来,可以从相同的. $newRootDomDocumentimportNode$newRoot$doc

让我们知道这是否适合您。干杯。

编辑:在版本 20031129 中注意到,如果设置,则在您最终调用时DomDocument::$formatOutput不会格式化输出$newRoot$doc->saveXML()

于 2014-10-03T23:24:50.097 回答