13

I'm a little confused as to how I can delete a parent node of something which I can find via an xpath search:

$xml = simplexml_load_file($filename);
$data = $xml->xpath('//items/info[item_id="' . $item_id . '"]');
$parent = $data[0]->xpath("parent::*");
unset($parent);

So, it finds the item id, no problems there - but the unset isn't getting rid of this <items> node. All I want to do is remove the <items>...</items> for this product. Obviously, there are loads of <items> nodes in the xml file so it can't do unset($xml->data->items) as that would delete everything.

Any ideas much appreciated :-)

bad_coder
  • 11,289
  • 20
  • 44
  • 72
Peter John
  • 1,859
  • 4
  • 15
  • 14

4 Answers4

14
<?php
$xml = new SimpleXMLElement('<a><b/></a>');
unset($xml->b);
echo $xml->asxml();

this works as intended (removing the <b/> element fromt he document) because the __unset() method (or the equivalent in the modules code) is called.
But when you call unset($parent); it only removes the object reference stored in $parent, but it doesn't affect the object itself or the document stored in $xml. I'd revert to DOMDocument for this.

<?php
$doc = new DOMDOcument;
$doc->loadxml('<foo>
  <items>
    <info>
      <item_id>123</item_id>
    </info>
  </items>
  <items>
    <info>
      <item_id>456</item_id>
    </info>
  </items>
  <items>
    <info>
      <item_id>789</item_id>
    </info>
  </items>
</foo>');
$item_id = 456;

$xpath = new DOMXpath($doc);
foreach($xpath->query('//items[info/item_id="' . $item_id . '"]') as $node) {
  $node->parentNode->removeChild($node);
}
echo $doc->savexml();

prints

<?xml version="1.0"?>
<foo>
  <items>
    <info>
      <item_id>123</item_id>
    </info>
  </items>

  <items>
    <info>
      <item_id>789</item_id>
    </info>
  </items>
</foo>
VolkerK
  • 95,432
  • 20
  • 163
  • 226
  • Cool thanks :-) The thing is though, i have the equivalent of 100,000 tags. I just want to delete the one which contains the product i have found via xpath. – Peter John Mar 14 '10 at 14:25
  • The example code only deletes one specific element, the one having item_id=456. What different behavior do you need? – VolkerK Mar 14 '10 at 14:34
13

It works like this for me. Not unset($parent); but unset($parent[0]);:

$res    = $xml->xpath('//key/k[. = "string"]/parent::*');
$parent = $res[0];
unset($parent[0]);

This works by creating a self-reference to the simplexml-element in $parent (or $res[0]).

For a more detailed explanation please see a related answer in the related question Remove a child with a specific attribute, in SimpleXML for PHP.

Community
  • 1
  • 1
Max
  • 153
  • 1
  • 7
2

One way is to import the SimpleXML node into DOMDocument and then remove within DOMDocument. Not really straight forward, but it works:

$xml = simplexml_load_file($filename);

$result = $xml->xpath("/cardsets/cardgroup");

foreach ($result as $el)
{
    if ($el['id'] == $id)
    {
        $domRef = dom_import_simplexml($el);
        $domRef->parentNode->removeChild($domRef);
        $dom = new DOMDocument('1.0');
        $dom->preserveWhiteSpace = false;
        $dom->formatOutput = true;
        $dom->loadXML($xml->asXML());
        $dom->save($filename);
        break;
    }
}
hakre
  • 193,403
  • 52
  • 435
  • 836
cemil
  • 21
  • 2
1

I'd surely approach this problem as a filtering one - not a removing one.

Thus, copying needed nodes into another string or build up another XML document for that matter. You know what tools you use for such scenarios.

I think this not only solves your problem, but probably keeps your easier to read and understand. Not sure about performance hits, though. Tell us how many nodes you regularly work with.

pestaa
  • 4,749
  • 2
  • 23
  • 32
  • Yeah good points - but, this would result in a considerable performance hit. There can be up to 100,000 items, so filtering doesnt seem like the best option to me as i just want to get rid of one item. All i need to find is the array index of 'items' for the searched product. This way i could do unset($xml->data->items[x]); which would work – Peter John Mar 14 '10 at 13:46