How to Rename a Tag in Simplexml Through a Dom Object

How do you rename a tag in SimpleXML through a DOM object?

Here's what's probably the simplest way to copy a node's children and attributes without using XSLT:

function clonishNode(DOMNode $oldNode, $newName, $newNS = null)
{
if (isset($newNS))
{
$newNode = $oldNode->ownerDocument->createElementNS($newNS, $newName);
}
else
{
$newNode = $oldNode->ownerDocument->createElement($newName);
}

foreach ($oldNode->attributes as $attr)
{
$newNode->appendChild($attr->cloneNode());
}

foreach ($oldNode->childNodes as $child)
{
$newNode->appendChild($child->cloneNode(true));
}

$oldNode->parentNode->replaceChild($newNode, $oldNode);
}

Which you can use this way:

$dom = new DOMDocument;
$dom->loadXML('<foo><bar x="1" y="2">x<baz/>y<quux/>z</bar></foo>');

$oldNode = $dom->getElementsByTagName('bar')->item(0);
clonishNode($oldNode, 'BXR');

// Same but with a new namespace
//clonishNode($oldNode, 'newns:BXR', 'http://newns');

die($dom->saveXML());

It will replace the old node with a clone with a new name.

Attention though, this is a copy of the original node's content. If you had any variable pointing to the old nodes, they are now invalid.

PHP + XML - how to rename and delete XML elements using SimpleXML or DOMDocument?

Using recursion, you can create a brand new document based on the input, solving all your points at once:

Code

<?php

$input = file_get_contents('http://www.fluffyduck.com.au/sampleXML.xml');
$inputDoc = new DOMDocument();
$inputDoc->loadXML($input);

$outputDoc = new DOMDocument("1.0", "utf-8");
$outputDoc->appendChild($outputDoc->createElement("root"));

function ConvertUserToItem($outputDoc, $inputNode, $outputNode)
{
if ($inputNode->hasChildNodes())
{
foreach ($inputNode->childNodes as $inputChild)
{
if (strtolower($inputChild->nodeName) == "user")
{
$outputChild = $outputDoc->createElement("item");
$outputNode->appendChild($outputChild);
// read input attributes and convert them to nodes
if ($inputChild->hasAttributes())
{
$outputContent = $outputDoc->createElement("content");
foreach ($inputChild->attributes as $attribute)
{
if (strtolower($attribute->name) != "id")
{
$outputContent->appendChild($outputDoc->createElement($attribute->name, $attribute->value));
}
else
{
$outputChild->setAttribute($attribute->name, $attribute->value);
}
}
$outputChild->appendChild($outputContent);
}
// recursive call
ConvertUserToItem($outputDoc, $inputChild, $outputChild);
}
}
}
}

ConvertUserToItem($outputDoc, $inputDoc->documentElement, $outputDoc->documentElement);

header("Content-Type: text/xml; charset=" . $outputDoc->encoding);
echo $outputDoc->saveXML();
?>

Output

<?xml version="1.0" encoding="utf-8"?>
<root>
<item id="41">
<content>
<username>bsmain</username>
<firstname>Boss</firstname>
<lastname>MyTest</lastname>
<fullname>Test Name</fullname>
<email>lalal@test.com</email>
<logins>1964</logins>
<lastseen>11/09/2012</lastseen>
</content>
<item id="61">
<content>
<username>underling</username>
<firstname>Under</firstname>
<lastname>MyTest</lastname>
<fullname>Test Name</fullname>
<email>lalal@test.com</email>
<logins>4</logins>
<lastseen>08/09/2009</lastseen>
</content>
</item>
...

Change NodeName of an XML tag element using MSXML

According to Document Object Model you can't rename a node.

See: http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1950641247

renameNode is possible in DOM Level 3 but not in MSXML library

Parsing inline tags with SimpleXML

I found a way to do this using DOMElement.

One way to replace the element is by cloning it with a different name/attributes. Here is is a way to do this, using the accepted answer given on How do you rename a tag in SimpleXML through a DOM object?

function clonishNode(DOMNode $oldNode, $newName, $replaceAttrs = [])
{
$newNode = $oldNode->ownerDocument->createElement($newName);
foreach ($oldNode->attributes as $attr)
{
if (isset($replaceAttrs[$attr->name]))
$newNode->setAttribute($replaceAttrs[$attr->name], $attr->value);
else
$newNode->appendChild($attr->cloneNode());
}
foreach ($oldNode->childNodes as $child)
$newNode->appendChild($child->cloneNode(true));
$oldNode->parentNode->replaceChild($newNode, $oldNode);
}

Now, we use this function to clone the inline element with a new element and attribute name. Here comes the tricky part: iterating over all the nodes will not work as expected. The length of the selected nodes will change as you clone them, as the original node is removed. Therefore, we only select the first element until there are no elements left to clone.

$xml = '<element>
random text with <inlinetag src="http://url.com/">inline</inlinetag> XML to parse
</element>';
$dom = new DOMDocument;
$dom->loadXML($xml);
$nodes= $dom->getElementsByTagName('inlinetag');
echo $dom->saveXML(); //<element>random text with <inlinetag src="http://url.com/">inline</inlinetag> XML to parse</element>
while($nodes->length > 0) {
clonishNode($nodes->item(0), 'a', ['src' => 'href']);
}
echo $dom->saveXML(); //<element>random text with <a href="http://url.com/">inline</a> XML to parse</element>

That's it! All that's left to do is getting the content of the element tag.

How to get the content of two xml tags even when they have another xml tags inside them (PHP-XML)

For the second part, you could use something like that:

<?php
$xml = "<TEXT>Well, I need some help as you <CUSTOMTAG>can</CUSTOMTAG> see, <CUSTOMTAG>maybe</CUSTOMTAG>.</TEXT>";

//1st part:
$dom = new DOMDocument();
$dom->loadXML($xml);
$xPath = new DOMXPath($dom);
foreach ($xPath->query('//TEXT') as $textNode) {
echo $textNode->textContent;
}
// 2nd part:
foreach ($xPath->query('//TEXT/CUSTOMTAG') as $find) {
$find_value = $find->nodeValue;
$replace = $dom->createDocumentFragment();
$replace->appendXML('<e>'.$find_value.'</e>');
$find->parentNode->insertBefore($replace, $find);
$parentnode = $find->parentNode;
$parentnode->removeChild($find);
}
$result = $dom->saveHTML();
echo $result;
?>

How to replace XML node with SimpleXMLElement PHP

Similar to what has been outlined in SimpleXML: append one tree to another you can import those nodes into DOMDocument because as you write:

"The class SimpleXMLElement dont have a method 'replaceChild' like the DOM."

So when you import into DOM you can use those:

$xml1 = simplexml_load_string($string1);
$xml2 = simplexml_load_string($string2);

$domToChange = dom_import_simplexml($xml1->map->operationallayers);
$domReplace = dom_import_simplexml($xml2);
$nodeImport = $domToChange->ownerDocument->importNode($domReplace, TRUE);
$domToChange->parentNode->replaceChild($nodeImport, $domToChange);

echo $xml1->asXML();

Which gives you the following output (non-beautified):

<?xml version="1.0"?>
<root>
<map>
<operationallayers>
<layer label="Teste1" type="feature" visible="false" useproxy="true" usePopUp="all" url="http://stackoverflow.com"/>
<layer label="Teste2" type="dynamic" visible="false" useproxy="true" usePopUp="all" url="http://google.com"/>
</operationallayers>
</map>
</root>

Additionally you can then take this and add the operation to your SimpleXMLElement so that it's easily wrapped. This works by extending from SimpleXMLElement:

/**
* Class MySimpleXMLElement
*/
class MySimpleXMLElement extends SimpleXMLElement
{
/**
* @param SimpleXMLElement $element
*/
public function replace(SimpleXMLElement $element) {
$dom = dom_import_simplexml($this);
$import = $dom->ownerDocument->importNode(
dom_import_simplexml($element),
TRUE
);
$dom->parentNode->replaceChild($import, $dom);
}
}

Usage Example:

$xml1 = simplexml_load_string($string1, 'MySimpleXMLElement');
$xml2 = simplexml_load_string($string2);

$xml1->map->operationallayers->replace($xml2);

Related: In SimpleXML, how can I add an existing SimpleXMLElement as a child element?.

Last time I extended SimpleXMLElement on Stackoverflow was in an answer to the "Read and take value of XML attributes" question.



Related Topics



Leave a reply



Submit