开发者

How to extract DOM childNodes or rename a element without using iteration?

$xml = '&开发者_开发百科lt;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);

the above will print back

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

assume need to replace the p node/eleemnt to new_p what is the ideal way except do a loop like below? (below is doable)

$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();

expected results

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


As Mark already pointed out, manipulating XML is easiest with XSLT. And you don't have to write any loops, the thinking is done by the XSLT processor of your choice.

A simple how-to with XSLT

Here's how the XSLT might look like (Google for "Identity transform XSLT" for some tutorials).

The basics are simple: this type of XSLT transformation copies everything as-is, unless there's a specific rule (template-match in XSLT) that specifies an exception (in this case for <p> elements). Note: it doesn't matter how deep your p-tags are nested, which makes it ideal for transforming 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>

Calling XSLT from PHP

This is relatively straightforward, since PHP has a built-in module for XSL. Here's how you can do it (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);
} 

Output is as expected (optional indentation can be set with <xsl:output indent="yes"/>:

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

As you can see: no loops or iterations ;)

PS: XSLT is a widely adopted and stable standard. You don't have to worry about proper escaping, parsing issues with CDATA sections or entities, because XSLT guarantees the output to be valid XML. This saves a whole lot of headaches as opposed to doing this by hand.


Is XSLT not perfect for this kind of operation?

How to rename elements with XSLT


While a loop is an obvious solutions, certain situations might prevent this or make it inapplicable; although I don't know of one. Alternatively it could be achieved by manipulating a string, the input XML string or the output XML from $new_doc->saveXML() method, depending on whichever you can use. I would go with str_ireplace, or regular expressions if tags contain attributes, particularly preg_replace with i modifier flag for case insensitive search. I could provide examples that way if you're interested in this technique.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜