How to traverse SimpleXML to edit text nodes?

前端 未结 1 456
暗喜
暗喜 2021-01-27 07:14

I need to implement the following algorithm with SimpleXML:

  1. put a XML fragment string into a SimpleXML object;
  2. traverse all the nodes, selecting text node
相关标签:
1条回答
  • 2021-01-27 07:45

    You can override the text content of a node returned by a SimpleXML XPath query by assigning to $node[0], e.g.

    foreach ( $sx->xpath('//text()') as $text_node )
    {
        $text_node[0] = 'Hello';
    }
    

    However, beware that SimpleXML does not really have a representation of a text node per se, so this kind of loop will behave oddly if there are both child elements and text within an element.

    For instance given the XML <a><b>foo<c />bar</b><b>baz quux</b></a>, the two text nodes containing foo and bar will both be represented in SimpleXML by the first <b> element, the entire contents of which will be replaced by 'Hello', twice over, as shown in the below (live demo here). Using a counter variable in the substituted text, we can see clearly what's happening - the desired output would be <a><b>Hello 1<c />Hello 2</b><b>Hello 3</b></a>, but the actual result is <a><b>Hello 2</b><b>Hello 3</b></a>.

    $sx = simplexml_load_string('<a><b>foo<c />bar</b><b>baz quux</b></a>');
    
    $counter = 1;
    foreach ( $sx->xpath('//text()') as $text_node )
    {
         $text_node[0] = 'Hello ' . $counter++;
    }
    
    echo $sx->asXML();
    

    This kind of manipulation, at least as you've framed the problem (finding text nodes, rather than iterating, possibly recursively, over a particular set of elements), is much more suited to the DOM API rather than SimpleXML. Bear in mind that there is no performance difference between the two (they are both wrappers around the same XML parser), and that you can combine operations using the two APIs on the same document by using simplexml_import_dom() and dom_import_simplexml(), again without additional overhead as the document doesn't need to be re-parsed.

    Here is the above example fixed by using a mixture of SimpleXML and DOM (live demo). If this were the whole code, you could just parse with DOM directly, but this demonstrates how easy they are to mix if you already have other code manipulating this document with SimpleXML. Note that at the end, we output the XML using the original SimpleXML object - we don't need to run simplexml_import_dom($dom), because both objects refer to the same parsed "document" in memory.

    $sx = simplexml_load_string('<a><b>foo<c />bar</b><b>baz quux</b></a>');
    $dom = dom_import_simplexml($sx);
    
    $counter = 1;
    $xpath = new DOMXpath($dom->ownerDocument);
    foreach ( $xpath->query('//text()') as $text_node )
    {
         $text_node->nodeValue = 'Hello ' . $counter++;
    }
    
    echo $sx->asXML();
    
    0 讨论(0)
提交回复
热议问题