1

I'm trying to figure out how to rename a node in XML using PHP?

I Have come this far:

$dom = new DOMDocument( '1.0' );
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;

// load the xml file
$dom->loadXML( '<?xml version="1.0" encoding="ISO-8859-1"?>
<library>
  <data>
    <invite>
      <username>jmansa</username>
      <userid>1</userid>
    </invite>
    <update>1</update>
  </data>
</library>', LIBXML_NOBLANKS );
$xpath = new DOMXPath($dom);

//find all 'data' nodes.
$node = $xpath->query("//data");

// if found
if( $node->length ) {
    foreach ($node as $n) {
       // RENAME HERE? //
    }
}

echo "<xmp>". $dom->saveXML() ."</xmp>";

Now, I want to rename <data> to <invites>. Can this be done and if yes, how?

0

2 Answers 2

1

A Node's name ("data" or "invites" respectively) cannot be renamed via the DOM because the Node::nodeName property is read-only.

You can create a new node named "invites", append it before the "data" node, move the children of "data" to the new "invites" node, remove the "data" node, and then output the tree to get your result.

Example:

<?php
// Create a test document.
    $dom = new DOMDocument( '1.0' );
    $dom->preserveWhiteSpace = false;
    $dom->formatOutput = true;

// Load the xml file.
    $dom->loadXML('<?xml version="1.0" encoding="ISO-8859-1"?'.'>
    <library>
      <data attr1="1" attr2="2">
        <invite>
          <username>jmansa</username>
          <userid>1</userid>
        </invite>
        <update>1</update>
      </data>
    </library>', LIBXML_NOBLANKS );
    $xpath = new DOMXPath($dom);

    // Convert <data> to <invites>.
    if ($dataNode = $xpath->query("/library/data")->item(0))
    {
    // Create the <invites> node.
        $invitesNode = $dom->createElement('invites');
        $dataAttrs   = $dataNode->attributes;
        foreach ($dataAttrs as $dataAttr)
        {   $invitesNode->setAttributeNodeNS($dataAttr->cloneNode());   }
        $dom->documentElement->appendChild($invitesNode);

    // Move the <data> children over.
        if ($childNodes = $xpath->query("/library/data/*"))
        {
            foreach ($childNodes as $childNode)
            {   $invitesNode->appendChild($childNode);  }
        }

    // Remove <data>.
        $dataNode->parentNode->removeChild($dataNode);
    }

// Test the result.
    echo $dom->saveXML();
?>
Sign up to request clarification or add additional context in comments.

8 Comments

Wow... That sounds like a bit of work... Can't str_replace be used in some way?
It's not that bad. I'll edit my post with an example. I wouldn't advise using str_replace unless you are absolutely certain <data> and later </data> will only exist in that specific context (rare).
Ok, allthough I'm pretty sure it will allways be as in example. Looking forward to your example :-)
Example added. Notice that I really only added three sections after starting with your code. Hopefully it will help.
It works allmnost perfect!? If there is no <data> field it adds an <invites>?
|
0

My solution, with extended test case:

// Changes the name of element $element to $newName.
function renameElement($element, $newName) {
  $newElement = $element->ownerDocument->createElement($newName);
  $parentElement = $element->parentNode;
  $parentElement->insertBefore($newElement, $element);

  $childNodes = $element->childNodes;
  while ($childNodes->length > 0) {
    $newElement->appendChild($childNodes->item(0));
  }

  $attributes = $element->attributes;
  while ($attributes->length > 0) {
    $attribute = $attributes->item(0);
    if (!is_null($attribute->namespaceURI)) {
      $newElement->setAttributeNS('http://www.w3.org/2000/xmlns/',
                                  'xmlns:'.$attribute->prefix,
                                  $attribute->namespaceURI);
    }
    $newElement->setAttributeNode($attribute);
  }

  $parentElement->removeChild($element);
}

function prettyPrint($d) {
  $d->formatOutput = true;
  echo '<pre>'.htmlspecialchars($d->saveXML()).'</pre>';
}

$d = new DOMDocument( '1.0' );
$d->loadXML('<?xml version="1.0"?>
<library>
  <data a:foo="1" x="bar" xmlns:a="http://example.com/a">
    <invite>
      <username>jmansa</username>
      <userid>1</userid>
    </invite>
    <update>1</update>
  </data>
</library>');

$xpath = new DOMXPath($d);
$elements = $xpath->query('/library/data');
if ($elements->length == 1) {
  $element = $elements->item(0);
  renameElement($element, 'invites');
}

prettyPrint($d);

By the way, I added this solution as a comment to the PHP documentation for DOMElement.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.