我希望尝试更改XML输出,以便元素结构发生变化,某些CDATA变为attribute
而不是<element>
鉴于XML stack.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的新手,非常感谢任何建议!
我如何将子结构和元素更改为所需的输出?
一如既往地感谢您的阅读!
编辑: Parfait给出了一个很好的解释,并展示了XSLT的强大功能,但我试图让这个只使用纯php作为php / DOM的学习练习。任何人都可以帮助使用PHP转换它吗?
答案 0 :(得分:1)
考虑XSLT,这是一种用于转换XML文档的特殊用途声明性语言。 PHP可以运行带有php-xsl
扩展名的XSLT 1.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>
答案 1 :(得分:1)
对于纯 PHP DOM解决方案,请考虑使用DOMDocument
,createElement
和{{1}创建一个新的appendChild
迭代旧文档的值} 方法。在创建具有项目节点值的元素之前,需要多个嵌套setAttribute
逻辑来检查节点的存在,否则会引发未定义警告。
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
,XSLT不需要它们。实际上,使用if
,我们可以比较脚本运行时。在enlargegen microtime
下面:
stack.xml
在1,000个节点行,XSLT证明比DOM更快:
$time_start = microtime(true);
...
echo "Total execution time in seconds: " . (microtime(true) - $time_start) ."\n";
在2,000个节点线上,XSLT仍然比DOM快约2倍:
# XSLT VERSION
Total execution time in seconds: 0.0062189102172852
# DOM VERSION
Total execution time in seconds: 0.013695955276489
在10,000个节点行,XSLT现在变得比DOM快一点。 DOM追赶的原因可能是由于XSLT 1.0为较大的文件维护的内存效率低下,尤其是(&gt; 100 MB)。但可以说这个用例,XSLT方法是一个更容易维护和阅读的PHP脚本:
# XSLT VERSION
Total execution time in seconds: 0.014697074890137
# DOM VERSION
Total execution time in seconds: 0.031282186508179