如何提取DOM childNodes或重命名元素而不使用迭代?

时间:2011-02-21 20:00:05

标签: php xml dom

$xml = '<p><a>1</a><b><c>1</c></b></p>';
$dom = new DomDocument;
$dom->loadXML($xml);
$p   = $dom->childNodes->item(0);
echo $dom->saveXML($p);

以上将打印回来

<p>
  <a>1</a>
  <b><c>1</c></b>
</p>

假设需要将p节点/ eleemnt替换为new_p 什么是理想的方式,除了像下面的循环? (下面是可行的)

$fragment = '';
foreach ($p->childNodes as $a)
{
  $fragment .= $dom->saveXML($a);
}

$new_doc = new DomDocument;
$new_doc->loadXML('<new_node/>');
$f = $new_doc->createDocumentFragment();
$f->appendXML($fragment);
$new_doc->documentElement->appendChild($f);
echo $new_doc->saveXML();

预期结果

<new_node><a>1</a><b><c>1</c></b></new_node>

3 个答案:

答案 0 :(得分:3)

正如Mark已经指出的那样,使用XSLT操作XML是最简单的。而且你不必编写任何循环,思考是由你选择的XSLT处理器完成的。

使用XSLT的简单方法

以下是XSLT的外观(对于某些教程,Google为“Identity transform XSLT”)。

基础知识很简单:这种类型的XSLT转换按原样复制所有内容,除非有特定规则(XSLT中的模板匹配)指定异常(在本例中为<p>元素)。注意:嵌入p标签的深度并不重要,这使其成为转换XML的理想选择。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <!-- identity transform -->
    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <!-- rename "p" with "new_p", copy everything inside p -->
    <xsl:template match="p">
        <new_p>
            <xsl:apply-templates select="@* | node()"/>
         </new_p>
    </xsl:template>

</xsl:stylesheet>

从PHP调用XSLT

这是相对简单的,因为PHP有​​一个内置的XSL模块。这是你如何做到的(here's more information):

// create an XSLT processor and load the stylesheet as a DOM 
$xproc = new XsltProcessor();
$xslt = new DomDocument;
$xslt->load('yourstylesheet.xslt');    // this contains the code from above
$xproc->importStylesheet($xslt);


// your DOM or the source XML (copied from your question)
$xml = '<p><a>1</a><b><c>1</c></b></p>';
$dom = new DomDocument;
$dom->loadXML($xml);

// do the transformation
if ($xml_output = $xproc->transformToXML($dom)) {
    echo $xml_output;
} else {
    trigger_error('Oops, XSLT transformation failed!', E_USER_ERROR);
} 

输出符合预期(可以使用<xsl:output indent="yes"/>设置可选缩进:

<new_p>
    <a>1</a>
    <b><c>1</c></b>
</new_p>

如您所见:没有循环或迭代;)

PS:XSLT是一种广泛采用且稳定的标准。您不必担心正确转义,解析CDATA部分或实体的问题,因为XSLT保证输出是有效的XML。与手工操作相比,这节省了许多麻烦。

答案 1 :(得分:2)

XSLT不适合这种操作吗?

How to rename elements with XSLT

答案 2 :(得分:-3)

虽然循环是一个明显的解决方案,但某些情况可能会阻止这种情况或使其不适用;虽然我不知道一个。或者,可以通过操作字符串,输入XML字符串或$new_doc->saveXML()方法的输出XML来实现,具体取决于您可以使用的方法。 如果标签包含属性,我会使用str_ireplace或正则表达式,特别是preg_replacei修饰符标志,用于不区分大小写的搜索。如果你对这种技术感兴趣,我可以提供一些例子。

相关问题