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

时间:2017-01-27 00:53:04

标签: php xml dom

我希望尝试更改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转换它吗?

2 个答案:

答案 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解决方案,请考虑使用DOMDocumentcreateElement和{{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