Remove a child with a specific attribute, in SimpleXML for PHP

后端 未结 17 2311
星月不相逢
星月不相逢 2020-11-22 02:50

I have several identical elements with different attributes that I\'m accessing with SimpleXML:


    
    

        
相关标签:
17条回答
  • 2020-11-22 03:13

    If you extend the base SimpleXMLElement class, you can use this method:

    class MyXML extends SimpleXMLElement {
    
        public function find($xpath) {
            $tmp = $this->xpath($xpath);
            return isset($tmp[0])? $tmp[0]: null;
        }
    
        public function remove() {
            $dom = dom_import_simplexml($this);
            return $dom->parentNode->removeChild($dom);
        }
    
    }
    
    // Example: removing the <bar> element with id = 1
    $foo = new MyXML('<foo><bar id="1"/><bar id="2"/></foo>');
    $foo->find('//bar[@id="1"]')->remove();
    print $foo->asXML(); // <foo><bar id="2"/></foo>
    
    0 讨论(0)
  • 2020-11-22 03:13

    Since I encountered the same fatal error as Gerry and I'm not familiar with DOM, I decided to do it like this:

    $item = $xml->xpath("//seg[@id='A12']");
    $page = $xml->xpath("/data");
    $id = "A12";
    
    if (  count($item)  &&  count($page) ) {
        $item = $item[0];
        $page = $page[0];
    
         // find the numerical index within ->children().
        $ch = $page->children();
        $ch_as_array = (array) $ch;
    
        if (  count($ch_as_array)  &&  isset($ch_as_array['seg'])  ) {
            $ch_as_array = $ch_as_array['seg'];
            $index_in_array = array_search($item, $ch_as_array);
            if (  ($index_in_array !== false)
              &&  ($index_in_array !== null)
              &&  isset($ch[$index_in_array])
              &&  ($ch[$index_in_array]['id'] == $id)  ) {
    
                 // delete it!
                unset($ch[$index_in_array]);
    
                echo "<pre>"; var_dump($xml); echo "</pre>";
            }
        }  // end of ( if xml object successfully converted to array )
    }  // end of ( valid item  AND  section )
    
    0 讨论(0)
  • 2020-11-22 03:15

    I believe Stefan's answer is right on. If you want to remove only one node (rather than all matching nodes), here is another example:

    //Load XML from file (or it could come from a POST, etc.)
    $xml = simplexml_load_file('fileName.xml');
    
    //Use XPath to find target node for removal
    $target = $xml->xpath("//seg[@id=$uniqueIdToDelete]");
    
    //If target does not exist (already deleted by someone/thing else), halt
    if(!$target)
    return; //Returns null
    
    //Import simpleXml reference into Dom & do removal (removal occurs in simpleXML object)
    $domRef = dom_import_simplexml($target[0]); //Select position 0 in XPath array
    $domRef->parentNode->removeChild($domRef);
    
    //Format XML to save indented tree rather than one line and save
    $dom = new DOMDocument('1.0');
    $dom->preserveWhiteSpace = false;
    $dom->formatOutput = true;
    $dom->loadXML($xml->asXML());
    $dom->save('fileName.xml');
    

    Note that sections Load XML... (first) and Format XML... (last) could be replaced with different code depending on where your XML data comes from and what you want to do with the output; it is the sections in between that find a node and remove it.

    In addition, the if statement is only there to ensure that the target node exists before trying to move it. You could choose different ways to handle or ignore this case.

    0 讨论(0)
  • 2020-11-22 03:16

    Idea about helper functions is from one of the comments for DOM on php.net and idea about using unset is from kavoir.com. For me this solution finally worked:

    function Myunset($node)
    {
     unsetChildren($node);
     $parent = $node->parentNode;
     unset($node);
    }
    
    function unsetChildren($node)
    {
     while (isset($node->firstChild))
     {
     unsetChildren($node->firstChild);
     unset($node->firstChild);
     }
    }
    

    using it: $xml is SimpleXmlElement

    Myunset($xml->channel->item[$i]);
    

    The result is stored in $xml, so don’t worry about assigning it to any variable.

    0 讨论(0)
  • 2020-11-22 03:17

    While SimpleXML provides a way to remove XML nodes, its modification capabilities are somewhat limited. One other solution is to resort to using the DOM extension. dom_import_simplexml() will help you with converting your SimpleXMLElement into a DOMElement.

    Just some example code (tested with PHP 5.2.5):

    $data='<data>
        <seg id="A1"/>
        <seg id="A5"/>
        <seg id="A12"/>
        <seg id="A29"/>
        <seg id="A30"/>
    </data>';
    $doc=new SimpleXMLElement($data);
    foreach($doc->seg as $seg)
    {
        if($seg['id'] == 'A12') {
            $dom=dom_import_simplexml($seg);
            $dom->parentNode->removeChild($dom);
        }
    }
    echo $doc->asXml();
    

    outputs

    <?xml version="1.0"?>
    <data><seg id="A1"/><seg id="A5"/><seg id="A29"/><seg id="A30"/></data>
    

    By the way: selecting specific nodes is much more simple when you use XPath (SimpleXMLElement->xpath):

    $segs=$doc->xpath('//seq[@id="A12"]');
    if (count($segs)>=1) {
        $seg=$segs[0];
    }
    // same deletion procedure as above
    
    0 讨论(0)
提交回复
热议问题