1

I have an XML file that currently has 4 nodes with the same name: The file looks like this: (data.xml)

<?xml version="1.0"?>
<products>
<product>
<ItemId>531670</ItemId>
<modelNumber>METRA ELECTRONICS/MOBILE AUDIO</modelNumber>
<name>Buy</name>
<name>Car, Marine &amp; GPS</name>
<name>Car Installation Parts</name>
<name>Deck Installation Parts</name>
<name>Antennas &amp; Adapters</name>
</product>
</products>

There are 4 nodes with the same name. (the name node).

I then use this PHP code to replace the node names

 <?php 
/**
 * @param $xml string Your XML
 * @param $old string Name of the old tag
 * @param $new string Name of the new tag
 * @return string New XML
 */
function renameTags($xml, $old, $new)
{
$dom = new DOMDocument();
$dom->loadXML($xml);

$nodes = $dom->getElementsByTagName($old);
$toRemove = array();
foreach ($nodes as $node)
{
    $newNode = $dom->createElement($new);
    foreach ($node->attributes as $attribute)
    {
        $newNode->setAttribute($attribute->name, $attribute->value);
    }

    foreach ($node->childNodes as $child)
    {
        $newNode->appendChild($node->removeChild($child));
    }

    $node->parentNode->appendChild($newNode);
    $toRemove[] = $node;
}

foreach ($toRemove as $node)
{
    $node->parentNode->removeChild($node);
}

return $dom->saveXML();
}

  // Load XML from file data.xml
$xml = file_get_contents('data.xml');

$xml = renameTags($xml, 'name', 'newName');

echo $xml;
?> 

This function replaces all of the name nodes with newName; however, I want to only replace one instance of the name tag because I want to rename each of the name tags. If I call another $xml = renameTags($xml, 'name', 'newName2');

It wont work, it will only use the first instance of $xml.

Any Idea how I can change my code to allow me to replace each name node individually?

2
  • haha you were here earlier ... :) Commented Dec 7, 2011 at 4:56
  • I was. This website really helps to understand things. Commented Dec 7, 2011 at 4:58

1 Answer 1

2

If you simply want to rename the first node you encounter, add a break; statement at the end of your first foreach loop like this first example. However, this is horribly inefficient and a better way to do it is demonstrated in the second (CORRECT) example at the bottom.

THE WRONG WAY Everytime you replace XML like this an angel sheds a tear ...

$src = "
<products>
  <product>
    <ItemId>531670</ItemId>
    <modelNumber>METRA ELECTRONICS/MOBILE AUDIO</modelNumber>
    <name>Buy</name>
    <name>Car, Marine &amp; GPS</name>
    <name>Car Installation Parts</name>
    <name>Deck Installation Parts</name>
    <name>Antennas &amp; Adapters</name>
  </product>
</products>
";

function renameTags($xml, $old, $new)
{
  $dom = new DOMDocument();
  $dom->loadXML($xml);

  $nodes = $dom->getElementsByTagName($old);
  $toRemove = array();
  foreach ($nodes as $node)
  {
    $newNode = $dom->createElement($new);

    foreach ($node->attributes as $attribute)
    {
        $newNode->setAttribute($attribute->name, $attribute->value);
    }

    foreach ($node->childNodes as $child)
    {
        $newNode->appendChild($node->removeChild($child));
    }

    $node->parentNode->appendChild($newNode);
    $toRemove[] = $node;
    break;
  }

  foreach ($toRemove as $node)
  {
    $node->parentNode->removeChild($node);
  }

  $dom->formatOutput = TRUE;
  return $dom->saveXML();
}

$xml = renameTags($src, 'name', 'newName');
echo $xml;

THE CORRECT WAY

function renameTags($xml, $old, $new)
{
  $dom = new DOMDocument();
  $dom->loadXML($xml);

  // find the first node with the specified tag name
  $oldNode = $dom->getElementsByTagName($old)->item(0);

  // clone the node (deep copy)
  $doppelganger = $oldNode->cloneNode(TRUE);

  // import our cloned node to this dom document
  $doppelganger = $dom->importNode($doppelganger, true);

  // Create new node with the value from the copied node
  $newNode = $dom->createElement($new, $doppelganger->nodeValue);

  // update all the attributes of the new node with those from the copy
  foreach ($doppelganger->attributes as $attrName => $attrNode) {
    $newNode->setAttribute($attrName, $attrNode);
  }

  // append the newNode copy to the dom
  $oldNode->parentNode->appendChild($newNode);

  // remove the old node
  $oldNode->parentNode->removeChild($oldNode);

  $dom->formatOutput = TRUE;
  return $dom->saveXML();
}
Sign up to request clarification or add additional context in comments.

1 Comment

I tested the second script, and it is replacing the name node; however, it is also deleting the name node's value.

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.