I have a problem... I want to rename the tags in some XML files. An it works with this code:
$xml = file_get_contents('data/onlinekeystore.xml');
renameTags($xml, 'priceEUR', 'price', 'data/onlinekeystore.xml');
But if I want to rename another XML file it doens't work with the SAME method... See the example below. I have no idea why... Does anybody has an idea and can help me?
$xml = file_get_contents('data/g2a.xml');
renameTags($xml, 'name', 'title', 'data/g2a.xml');
Function Code:
function renameTags($xml, $old, $new, $path){
$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);
}
$dom->saveXML();
$dom->save($path);
}
onlinekeystore.xml Input:
<product>
<priceEUR>5.95</priceEUR>
</product>
onlinekeystore.xml Ouput:
<product>
<price>5.95</price>
</product>
g2a.xml Input:
<products>
<name><![CDATA[1 Random STEAM PREMIUM CD-KEY]]></name>
</products>
g2a.xml Ouput:
<products>
<name><![CDATA[1 Random STEAM PREMIUM CD-KEY]]></name>
</products>
Greetings
Consider a dynamic XSLT running the Identity Transform and then updates node names in a specific template anywhere in document. The sprintf
formats XSL string passing in $old
and $new
values. This process avoids any nested looping through entire tree and even pretty prints output no matter the format of input.
function renameTags($xml, $old, $new, $path){
// LOAD XML
$dom = new DOMDocument();
$dom->loadXML($xml);
// LOAD XSL
$xslstr = '<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" method="xml" cdata-section-elements="%2$s"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="%1$s">
<xsl:element name="%2$s">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:transform>';
$xsl = new DOMDocument();
$xsl->loadXML(sprintf($xslstr, $old, $new));
// INITIALIZE TRANSFORMER (REQUIRES php_xsl EXTENSION ENABLED IN .ini)
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
// TRANSFORM XML AND SAVE OUTPUT
$newXML = $proc->transformToXML($dom);
file_put_contents($path, $newXML);
}
Output
renameTags('<product><priceEUR>5.95</priceEUR></product>', 'priceEUR', 'price', 'data/onlinekeystore.xml');
// <?xml version="1.0" encoding="UTF-8"?>
// <product>
// <price><![CDATA[5.95]]></price>
// </product>
renameTags('<products><name><![CDATA[1 Random STEAM PREMIUM CD-KEY]]></name></products>', 'name', 'title', 'data/g2a.xml');
// <?xml version="1.0" encoding="UTF-8"?>
// <products>
// <title><![CDATA[1 Random STEAM PREMIUM CD-KEY]]></title>
// </products>
Note: In XSLT, the <![CDATA[...]]
tags are not preserved unless explicitly specified in <xsl:output>
. Right now, any new node's text is wrapped with it. Remove the output spec and no CData tags render. So either include such escape tags for all or none.