Удалить дочерний элемент с определенным атрибутом в SimpleXML для PHP - PullRequest
46 голосов
/ 04 ноября 2008

У меня есть несколько идентичных элементов с разными атрибутами, к которым я обращаюсь с помощью SimpleXML:

<data>
    <seg id="A1"/>
    <seg id="A5"/>
    <seg id="A12"/>
    <seg id="A29"/>
    <seg id="A30"/>
</data>

Мне нужно удалить определенный элемент seg с идентификатором «A12», как я могу это сделать? Я пробовал циклически проходить элементы seg и unset tting определенного элемента, но это не работает, элементы остаются.

foreach($doc->seg as $seg)
{
    if($seg['id'] == 'A12')
    {
        unset($seg);
    }
}

Ответы [ 17 ]

0 голосов
/ 09 марта 2016

Если вы хотите сократить список похожих (не уникальных) дочерних элементов, например элементов RSS-канала, вы можете использовать этот код:

for ( $i = 9999; $i > 10; $i--) {
    unset($xml->xpath('/rss/channel/item['. $i .']')[0]->{0});
}

Это сократит хвост RSS до 10 элементов. Я пытался удалить с

for ( $i = 10; $i < 9999; $i ++ ) {
    unset($xml->xpath('/rss/channel/item[' . $i . ']')[0]->{0});
}

Но это работает как-то случайным образом и обрезает только некоторые элементы.

0 голосов
/ 24 января 2016

С FluidXML вы можете использовать XPath для выбора удаляемых элементов.

$doc = fluidify($doc);

$doc->remove('//*[@id="A12"]');

https://github.com/servo-php/fluidxml


XPath //*[@id="A12"] означает:

  • в любой точке документа (//)
  • каждый узел (*)
  • с атрибутом id, равным A12 ([@id="A12"]).
0 голосов
/ 02 июля 2013

Поскольку я столкнулся с той же фатальной ошибкой, что и Джерри, и я не знаком с DOM, я решил сделать это так:

<code>$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 "
"; } } // конец (если объект xml успешно преобразован в массив) } // конец (действительный элемент И раздел)
0 голосов
/ 22 мая 2013

Я также боролся с этой проблемой, и ответ намного проще, чем приведенный здесь. Вы можете просто найти его с помощью xpath и сбросить его следующим способом:

unset($XML->xpath("NODESNAME[@id='test']")[0]->{0});

этот код будет искать узел с именем "NODESNAME" с атрибутом id "test" и удалять первый случай.

не забудьте сохранить xml, используя $ XML-> saveXML (...);

0 голосов
/ 21 августа 2009

Идея о вспомогательных функциях взята из одного из комментариев для DOM на php.net , а идея об использовании unset - из kavoir.com . Для меня это решение наконец-то сработало:

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

function unsetChildren($node)
{
 while (isset($node->firstChild))
 {
 unsetChildren($node->firstChild);
 unset($node->firstChild);
 }
}

используя его: $ xml - это SimpleXmlElement

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

Результат хранится в $ xml, поэтому не беспокойтесь о присвоении его любой переменной.

0 голосов
/ 11 октября 2009

Несмотря на то, что в SimpleXML нет подробного способа удаления элементов, вы можете удалить элементы из SimpleXML с помощью PHP unset(). Ключом к этому является управление целевым элементом. По крайней мере, один из способов нацеливания - это порядок элементов. Сначала узнайте порядковый номер элемента, который вы хотите удалить (например, с помощью цикла), затем удалите элемент:

$target = false;
$i = 0;
foreach ($xml->seg as $s) {
  if ($s['id']=='A12') { $target = $i; break; }
  $i++;
}
if ($target !== false) {
  unset($xml->seg[$target]);
}

Вы можете даже удалить несколько элементов, сохранив порядковый номер целевых элементов в массиве. Просто не забудьте выполнить удаление в обратном порядке (array_reverse($targets)), поскольку удаление элемента естественным образом уменьшает порядковый номер элементов, следующих за ним.

По общему признанию, это что-то вроде взлома, но, похоже, работает нормально.

0 голосов
/ 13 марта 2010

Ваш первоначальный подход был верным, но вы забыли одну маленькую вещь о foreach. Он не работает с исходным массивом / объектом, но создает копию каждого элемента во время итерации, поэтому вы удалили копию. Используйте ссылку как это:

foreach($doc->seg as &$seg) 
{
    if($seg['id'] == 'A12')
    {
        unset($seg);
    }
}
...