简体   繁体   中英

Delete XML element by value of child element

I have a xml file like:

<file>
    <data>
        <input name="id">1</input>
        <input name="name">Mike</input>
    </data>
    <data>
        <input name="id">2</input>
        <input name="name">Andree</input>
    </data>
</file>

How to delete element:data in value element:input 1 by attribute:id

I've read this example php-delete-xml-element but this do not have the same level of depth that I am looking for.

NB: I'm hoping to use DOMDocument

The way to achieve this with DOMDocument , similar to the very good example sevavietl posted, is to do the same thing and find our node using XPath .

There is a removeChild method on the DOMNode class, which our elements extend as a DOMElement . In order to remove a node we need to use the parent element containing the node we wish to remove. On all DOMNodes we have a property called parentNode and this property will always return the parent or null if there is no parent (when you reach the documentElement node or you've created a node and not inserted it for example).

// Load the dom document
$document = new DOMDocument();
$document->load('./path/to/xml.xml'); 
// use $document->loadXML($xml) if you're passing a XML string

// Create XPath for the document
$xpath = new DOMXpath($document);

// Use XPath to locate our node(s)
$nodelist = $xpath->query('.//input[@name="id"][contains(., '1')]/..', $document->documentElement);

// Iterate over our node list and remove the data
foreach ($nodelist as $dataNode) {
    if ($dataNode->parentNode === null) {
        continue; // Uh oh, no parent node?
    }
    // Get the data node parent (file) so we can call remove child
    $dataNode->parentNode->removeChild($dataNode);
}

All the real magic is in our XPath:

  • .//input from the context node, find input elements at any depth
  • [@name="id"] the nodes we're looking for must have a name attribute of id
  • [contains(.,'1')] The node must contain the text 1
  • /.. From the node we found, move up one level

The context node is always the second argument passed to the query method of XPath. So if we put it all together we get our XPath statement, find all input nodes with a name attribute equalling id and containing the text 1. From there move back up the DOM tree by one level.

If for example your DOM tree contains other elements that also include input elements with the same attributes and we only want to remove data elements, we can change the query to

.//data/input[@name="id"][contains(., '1')]/..

This now means find all input nodes which has a direct ancestor of data . Then apply our other conditions as before.

I know you wanted to do this with DOMDocument , but if you simply need to delete node, you can do this with SimpleXmlElement and XPath :

$xml = new SimpleXMLElement($xmlString);

$id = 1;

$data = $xml->xpath('//input[@name="id"][.=' . $id . ']/..');

if (isset($data[0])) {
    unset($data[0]->{0});
}

Here is working demo.

The magic with unset() is explained here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM