将元素更改为属性PHP/DOM(XML)



我想尝试更改XML输出,以便元素结构发生变化,并且某些CDATA变成attribute而不是<element>

给定XMLstack.xml:

<root>
<item>
<name>name</name>
<type>Type</type>
<dateMade>Datemade</dateMade>
<desc>Desc</desc>
</item>
....(more Items)...
</root>

我想将XML输出更改为stacksaved.xml:

<root>
<item>
<name>name</name>
<Itemtype type="Type">
<Itemdate dateMade="Datemade">
<desc>Desc</desc>
</Itemdate>
<Itemtype>
</item>
....(next item)....
</root>

到目前为止,我的PHP DOM看起来是这样的:

<?php
//create and load
$doc = new DOMDocument();
$doc->load('stack.xml');
$types=$doc->getElementsByTagName("type");
foreach ($types as $type)
{
$attribute=$doc->getElementsByTagName("type");
$doc->getElementsByTagName("type").setAttribute("$attribute"); 
}
$doc->save('stacksaved.xml'); //save the final results into xml file
?>

我一直收到错误:致命错误:调用未定义的函数setAttribute(),文档无论如何都没有保存或编辑。我真的是DOM/PHP的新手,如果有任何建议,我将不胜感激!

如何将子结构元素更改为所需输出?

一如既往地感谢您的阅读!

EDIT:Parfait给出了一个很好的解释,并展示了XSLT的强大功能,但我正试图让它只使用纯php作为php/DOM的学习练习来运行。有人能用PHP来帮助转换吗?

对于PHP DOM解决方案,请考虑使用createElementappendChildsetAttribute方法在旧文档的值上迭代创建一个新的DOMDocument。在创建具有项的节点值的元素之前,需要使用多个嵌套的if逻辑来检查节点的存在,否则会引发未定义的警告

$doc = new DOMDocument();
$doc->load('stack.xml');
// INITIALIZE NEW DOM DOCUMENT
$newdoc = new DOMDocument('1.0', 'UTF-8');
$newdoc->preserveWhiteSpace = false;
$newdoc->formatOutput = true;
// APPEND ROOT
$root= $newdoc->appendChild($newdoc->createElement("root"));
$items=$doc->getElementsByTagName("item");
// ITERATIVELY APPEND ITEM AND CHILDREN
foreach($items as $item){    
$ItemNode = $newdoc->createElement("item");
$root->appendChild($ItemNode);
if (count($item->getElementsByTagName("name")->item(0)) > 0) {
$ItemNode->appendChild($newdoc->createElement('name', $item->getElementsByTagName("name")->item(0)->nodeValue));
}
if (count($item->getElementsByTagName("type")->item(0)) > 0) {        
$ItemtypeNode = $ItemNode->appendChild($newdoc->createElement('Itemtype'));
$ItemtypeNode->setAttribute("type", $item->getElementsByTagName("type")->item(0)->nodeValue);
if (count($item->getElementsByTagName("dateMade")->item(0)) > 0) {
$ItemdateNode = $ItemtypeNode->appendChild($newdoc->createElement('Itemdate'));
$ItemdateNode->setAttribute("dateMade", $item->getElementsByTagName("dateMade")->item(0)->nodeValue);
if (count($item->getElementsByTagName("desc")->item(0)) > 0) {
$ItemdateNode->appendChild($newdoc->createElement('desc', $item->getElementsByTagName("desc")->item(0)->nodeValue));
}
}
}
}
// ECHO AND SAVE NEW DOC TREE
echo $newdoc->saveXML();
$newdoc->save($cd.'/ItemTypeDateMade_dom.xml'); 

输出

<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<name>name</name>
<Itemtype type="Type">
<Itemdate dateMade="Datemade">
<desc>Desc</desc>
</Itemdate>
</Itemtype>
</item>
</root>

正如前面的回答中所提到的,这里需要for和嵌套的if,而XSLT不需要它们。事实上,使用microtime,我们可以比较脚本运行时。放大stack.xml:下方

$time_start = microtime(true); 
...
echo "Total execution time in seconds: " . (microtime(true) - $time_start) ."n";

在1000个节点行中,XSLT比DOM:更快

# XSLT VERSION
Total execution time in seconds: 0.0062189102172852
# DOM VERSION
Total execution time in seconds: 0.013695955276489

在2000个节点行中,XSLT仍然比DOM:快大约两倍

# XSLT VERSION
Total execution time in seconds: 0.014697074890137
# DOM VERSION
Total execution time in seconds: 0.031282186508179

在10000个节点行中,XSLT现在比DOM稍微快一些。DOM赶上的原因可能是XSLT1.0为较大的文件(尤其是(>100MB))维护的内存效率低下。但可以说,对于这个用例,XSLT方法是一个更容易维护和阅读的PHP脚本:

# XSLT VERSION
Total execution time in seconds: 0.27568817138672
# DOM VERSION
Total execution time in seconds: 0.37149095535278

考虑XSLT,这是一种专门用于转换XML文档的声明性语言。PHP可以运行扩展名为php-xsl的XSLT1.0脚本(一定要在.ini文件中启用它)。使用这种方法,可以避免任何foreach循环或if逻辑的需要。

XSLT (另存为.xsl文件)

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="root">   
<xsl:copy>
<xsl:apply-templates select="item"/>
</xsl:copy>
</xsl:template>    
<xsl:template match="item">
<xsl:copy>
<xsl:copy-of select="name"/>
<Itemtype type="{type}">
<Itemdate dateMade="{dateMade}">
<xsl:copy-of select="desc"/>   
</Itemdate>
</Itemtype>    
</xsl:copy>   
</xsl:template>
</xsl:transform>

PHP

$doc = new DOMDocument();
$doc->load('stack.xml');
$xsl = new DOMDocument;
$xsl->load('XSLTScript.xsl');
// CONFIGURE TRANSFORMER
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl); 
// PROCESS TRANSFORMATION
$newXML = $proc->transformToXML($doc);
// ECHO STRING OUTPUT
echo $newXML;
// SAVE OUTPUT TO FILE
file_put_contents('Output.xml', $newXML);

输出

<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<name>name</name>
<Itemtype type="Type">
<Itemdate dateMade="Datemade">
<desc>Desc</desc>
</Itemdate>
</Itemtype>
</item>
</root>

最新更新