Php simplexml удалить узел

SimpleXML — удалить узел xpath

Я немного смущен тем, как я могу удалить родительский узел чего-то, что я могу найти с помощью поиска xpath:

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

Итак, он находит идентификатор элемента, проблем нет, но удаление не избавляет от этого узла . Все, что я хочу сделать, это удалить . для этого продукта. Очевидно, что в файле xml есть множество узлов , поэтому он не может выполнить unset($xml->data->items) , так как это удалит все. Любые идеи высоко ценятся 🙂

Ответы (4)

это работает как задумано (удаление элемента ‹b/› из документа), потому что вызывается метод __unset() (или его эквивалент в коде модуля).
Но когда вы вызываете unset($parent); , он удаляет только ссылку на объект, хранящуюся в $parent, но не влияет на объект себя или документ, хранящийся в $xml. Для этого я бы вернулся к DOMDocument.

loadxml('   123    456    789   '); $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(); 

Круто, спасибо 🙂 Дело в том, что у меня есть эквивалент 100 000 тегов ‹b›. Я просто хочу удалить тот, который содержит продукт, который я нашел через xpath. — person Peter John; 14.03.2010

Код примера удаляет только один конкретный элемент, имеющий item_id=456. Какое другое поведение вам нужно? — person VolkerK; 14.03.2010

Великолепно! Это именно то, что мне было нужно 🙂 Спасибо дружище! — person Peter John; 14.03.2010

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

Для этого создается самостоятельная ссылка на элемент simplexml в $parent (или $res[0] ). Более подробное объяснение см. в связанном ответе в соответствующем вопросе Удалить дочерний элемент с определенным атрибутом в SimpleXML для PHP.

Один из способов — импортировать узел SimpleXML в DOMDocument, а затем удалить его в DOMDocument. Не совсем прямо, но работает:

$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; > > 

Я бы, конечно, подошел к этой проблеме как к проблеме фильтрации, а не удаления. Таким образом, копирование необходимых узлов в другую строку или создание другого XML-документа в этом отношении. Вы знаете, какие инструменты вы используете для таких сценариев. Я думаю, что это не только решает вашу проблему, но, вероятно, упрощает чтение и понимание. Однако не уверен в производительности. Расскажите, со сколькими узлами вы регулярно работаете.

Да, хорошие моменты, но это приведет к значительному снижению производительности. Может быть до 100 000 элементов, поэтому фильтрация не кажется мне лучшим вариантом, поскольку я просто хочу избавиться от одного элемента. Все, что мне нужно найти, это индекс массива «элементов» для искомого продукта. Таким образом, я мог бы сделать unset($xml-›data-›items[x]); который будет работать — person Peter John; 14.03.2010

Читайте также:  Java lang nosuchfieldexception ucp

Источник

Удалить дочерний элемент с определенным атрибутом в SimpleXML для PHP

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

18 ответов

Хотя SimpleXML предоставляет способ удаления узлов XML, возможности его модификации несколько ограничены. Еще одно решение — использовать расширение DOM. dom_import_simplexml() поможет вам преобразовать ваш SimpleXMLElement в DOMElement . Просто пример кода (протестирован с PHP 5.2.5):

$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(); 
$segs=$doc->xpath('//seq[@id="A12"]'); if (count($segs)>=1) < $seg=$segs[0]; >// same deletion procedure as above 

Спасибо за это — изначально я был склонен избегать этого ответа, так как я хотел избежать использования DOM. Я попробовал несколько других ответов, которые не сработали, прежде чем, наконец, попробовать ваш — который работал безупречно. Любой, кто рассматривает возможность избежать этого ответа, сначала попробуйте его и посмотрите, не получается ли он именно то, что вы хотите. Я думаю, что меня оттолкнуло то, что я не осознавал, что dom_import_simplexml () по-прежнему работает с той же базовой структурой, что и simplexml, поэтому любые изменения в одном сразу влияют на другой, не нужно писать / читать или перезагружать.

Обратите внимание, что этот код удалит только первый встреченный элемент. Я подозреваю, что это потому, что изменение данных во время итерации делает недействительной позицию итератора, что приводит к завершению цикла foreach. Я решил эту проблему, сохранив dom-импортированные узлы в массиве, который затем перебрал, чтобы выполнить удаление. Не очень хорошее решение, но оно работает.

На самом деле вы можете удалить элементы SimpleXML, используя unset, но это в моем ответе;) stackoverflow.com/a/16062633/367456

Вопреки распространенному мнению в существующих ответах, каждый элемент Simplexml node может быть удален из документа сам по себе и unset() . Дело в том, что вам просто нужно понять, как работает SimpleXML.

Читайте также:  Php xmlreader open file

Сначала найдите элемент, который хотите удалить:

list($element) = $doc->xpath('/*/seg[@id="A12"]'); 

Затем удалите элемент, представленный в $element , вы отключите его самоописание:

Это работает, потому что первым элементом любого элемента является сам элемент в Simplexml (самореклама). Это связано с его магической природой, числовые индексы представляют элементы в любом списке (например, parent- > children), и даже один из них является таким списком.

Номера числовых индексов строки представляют атрибуты (в массиве) или дочерние элементы (в свойствах).

Поэтому числовые индексы в свойстве-доступ вроде:

Естественно, что этот пример xpath довольно прямолинейный (в PHP 5.4):

       DATA; $doc = new SimpleXMLElement($data); unset($doc->xpath('seg[@id="A12"]')[0]->); $doc->asXml('php://output'); 

Очень хорошо объяснил ответ. Одна деталь, которую я не сразу оценил, это то, что вы не можете просто вывести XPath из цикла, потому что удаление элемента внутри обычного цикла foreach ( $doc->seg as $seg ) сбивает с толку итератор (эмпирическое правило: don не изменяйте длину итератора в середине цикла). Реализация SimpleXML в XPath не имеет этой проблемы, потому что ее результаты представляют собой обычный массив несвязанных элементов.

@IMSoP: Для любого Traversable и этот вопрос (живые списки), я настоятельно рекомендую iterator_to_array в SimpleXML итераторов установить ключевой параметр FALSE , поскольку SimpleXMLElement использует имя-тег , как ключ , который часто является продублировать в таком перечислении , а затем эту функцию будет возвращать только последний из этих одноименных узлов, если второй параметр не FALSE .

$str = STR; $xml = simplexml_load_string($str); unset($xml –> a –> b –> c); // this would remove node c echo $xml –> asXML(); // xml document string without node c 

Это работает, только если имя узла уникально в наборе. Если это не так, вы в конечном итоге удаляете все узлы с одинаковыми именами.

@Dallas: Даллас: То, что вы комментируете, правильно, но оно также содержит решение. Как получить доступ только к первому элементу? Смотрите здесь: stackoverflow.com/a/16062633/367456

Я считаю, что ответ Стефана прав. Если вы хотите удалить только один node (а не все совпадающие узлы), вот еще один пример:

//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'); 

Обратите внимание, что разделы Load XML. (first) и Format XML. (последний) могут быть заменены другим кодом в зависимости от того, откуда взялись ваши XML-данные и что вы хотите делать с выходом; это промежутки между ними, которые находят node и удаляют его.

Читайте также:  Html img hover src

Кроме того, оператор if существует только для того, чтобы убедиться, что объект node существует, прежде чем пытаться его переместить. Вы можете выбрать различные способы обработки или игнорирования этого случая.

Обратите внимание, что xpath () возвращает пустой массив, если ничего не найдено, поэтому проверка $ target == false должна быть пустой ($ target). +1 за решение xpath

Если вы расширяете базовый класс SimpleXMLElement, вы можете использовать этот метод:

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 element with = new MyXML(''); $foo->find('//bar[@id="1"]')->remove(); print $foo->asXML(); //  

Он подвержен Fatal error: Call to a member function remove() on null каждый раз, когда $foo->find(‘//bar[@id=»1″]’) возвращает null .

$data = '     '; $doc = new SimpleXMLElement($data); $segarr = $doc->seg; $count = count($segarr); $j = 0; for ($i = 0; $i < $count; $i++) < if ($segarr[$j]['id'] == 'A12') < unset($segarr[$j]); $j = $j - 1; >$j = $j + 1; > echo $doc->asXml(); 

Чтобы удалить/сохранить узлы с определенным значением атрибута или попадать в массив значений атрибутов, вы можете расширить класс SimpleXMLElement как это (самая последняя версия в моем GitHub Gist):

class SimpleXMLElementExtended extends SimpleXMLElement < /** * Removes or keeps nodes with given attributes * * @param string $attributeName * @param array $attributeValues * @param bool $keep TRUE keeps nodes and removes the rest, FALSE removes nodes and keeps the rest * @return integer Number o affected nodes * * @example: $xml->o->filterAttribute('id', $products_ids); // Keeps only nodes with id attr in $products_ids * @see: http://stackoverflow.com/questions/17185959/simplexml-remove-nodes */ public function filterAttribute($attributeName = '', $attributeValues = array(), $keepNodes = TRUE) < $nodesToRemove = array(); foreach($this as $node) < $attributeValue = (string)$node[$attributeName]; if ($keepNodes) < if (!in_array($attributeValue, $attributeValues)) $nodesToRemove[] = $node; >else < if (in_array($attributeValue, $attributeValues)) $nodesToRemove[] = $node; >> $result = count($nodesToRemove); foreach ($nodesToRemove as $node) < unset($node[0]); >return $result; > > 

Затем, используя ваш XML- $doc вы можете удалить свой узел :

$data='      '; $doc=new SimpleXMLElementExtended($data); $doc->seg->filterAttribute('id', ['A12'], FALSE); 

или удалить несколько узлов :

$doc->seg->filterAttribute('id', ['A1', 'A12', 'A29'], FALSE); 

Для сохранения только и узлов и удаления остальных:

$doc->seg->filterAttribute('id', ['A5', 'A30'], TRUE); 

Источник

Оцените статью