- PHP — Удалить элемент XML
- 5 ответов
- How to delete / remove nodes in SimpleXML
- Related Posts
- What is PHP framework & Who are the Best PHP Frameworks?
- Cron jobs (Crontab jobs) tricks and tips on DreamHost
- 14 thoughts on “How to delete / remove nodes in SimpleXML”
- Удалить дочерний элемент с определенным атрибутом в SimpleXML для PHP
- 18 ответов
PHP — Удалить элемент XML
Так что я бы сказал, это что-то вроде удаления элемента, где время равно 1248083838. Я использую Simple XML до сих пор, и я только что понял, что он может делать все, кроме элементов delete. Итак, как бы я это сделал?
5 ответов
Вы можете использовать классы DOM в PHP. (http://us3.php.net/manual/en/intro.dom.php). Вам нужно будет прочитать XML-документ в памяти, использовать классы DOM для манипуляции, а затем вы можете сохранить XML по мере необходимости (до http или файла). DOMNode — это объект, который имеет функции удаления (для решения вашего вопроса). Это немного сложнее, чем SimpleXML, но как только вы привыкнете к нему, он гораздо более мощный (полу-взятый из примера кода на php.net)
load('theFile.xml'); $thedocument = $doc->documentElement; //this gives you a list of the messages $list = $thedocument->getElementsByTagName('message'); //figure out which ones you want -- assign it to a variable (ie: $nodeToRemove ) $nodeToRemove = null; foreach ($list as $domElement)< $attrValue = $domElement->getAttribute('time'); if ($attrValue == 'VALUEYOUCAREABOUT') < $nodeToRemove = $domElement; //will only remember last one- but this is just an example :) >> //Now remove it. if ($nodeToRemove != null) $thedocument->removeChild($nodeToRemove); echo $doc->saveXML(); ?>
Это должно дать вам немного идеи о том, как удалить элемент. Он распечатает XML без этого node. Если вы хотите отправить его в файл, просто напишите строку в файл.
У меня не было возможности проверить наличие незначительных синтаксических ошибок — но это должно дать вам что-то вроде того, что вам нужно. Я рекомендую не удалять ребенка в самом замке, потому что он портит внутренний счетчик foreach.
Дейв Морган прав, поскольку классы DOM более мощные, но если вы хотите придерживаться SimpleXML, попробуйте использовать функцию unset() на любом node. Например. unset ($ simpleXMLDoc- > node1- > child1), и это будет удалено из XML.
Я не пробовал это делать, но логика подсказывает, что все должно работать нормально. Звучит как быстро-грязная, но правильная работа для меня.
How to delete / remove nodes in SimpleXML
The other day I was testing and playing with SimpleXML to process some XML documents when I came to a problem — I realized that I needed to delete a node before dumping the XML object to text. However if you are familiar with SimpleXML, it doesn’t come with a down right way to remove nodes like DOMDocument does.
A few searches on Google neither helped — people were just saying how simple SimpleXML was and it simply could not fulfill a task like this.
You know what I did? Yes, just ‘unset‘ the node.
$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
Related Posts
What is PHP framework & Who are the Best PHP Frameworks?
Cron jobs (Crontab jobs) tricks and tips on DreamHost
14 thoughts on “How to delete / remove nodes in SimpleXML”
Oh so this is how to do it and it seems to be easy ïŠ Now I don’t need to bother our IT whenever I want to remove SimpleXML . Thanks for covering this topic dude ïŠ
The problem with this approach (and all the approaches I have seen) is that you need to know the the xml tree. What happens if you have multiple nodes of the same type? You can use:
unset($xmlNode->file[$ix]); but you still need to know the name of the node you want to delete (above it is “file”). Still this approach does allow you to iterate through nodes deleting the ones you want to: $ix = count($xmlNode->file);
for ($ix– ; $ix >= 0 ; $ix–)
$xmlSubNode = $xmlNode->file[$ix];
if (doYouWantToDeleteThisNode($xmlSubNode))
unset($xmlNode->file[$ix]);
>
> The reason I count backwards is because deleting the nodes causes the later indices to change. You can count up – you just need to make sure that you do not increment your counter variable (ix above) when you delete the node.
I have faced with the problem that David described, and found a simpler solution at php.net. Steps are as follows:
* Get the node object in any ways (eg. via xpath())
* import the node as DOM object
* perform the removal. As an example: list($theNodeToBeDeleted) = $myXml->xpath($someXPathQuery);
$oNode = dom_import_simplexml($theNodeToBeDeleted);
$oNode->parentNode->removeChild($oNode); Simple enaugh, huh?
I made a class extension based on the code in the last post. In my case I needed to remove nodes by specific attributes and values. So here’s my effort: class SimpleXMLExtend extends SimpleXMLElement
public function addCData($nodename,$cdata_text)
$node = $this->addChild($nodename); //Added a nodename to create inside the function
$node = dom_import_simplexml($node);
$no = $node->ownerDocument;
$node->appendChild($no->createCDATASection($cdata_text));
> public function removeNodeByAttrib($nodename,$attribute,$value) foreach ($this->xpath($nodename) as $key => $node) foreach($node->attributes() as $attrib => $val) if ($attrib == $attribute && $val == $value) $oNode = dom_import_simplexml($node);
>
>
>
$oNode->parentNode->removeChild($oNode);
> > Hope this may be helpful for anyone.
I’m using this class.
I always receive this error: Fatal error: Call to a member function removeChild() on a non-object on line 20. And this is line 20 : $oNode->parentNode->removeChild($oNode);
Удалить дочерний элемент с определенным атрибутом в 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.
Сначала найдите элемент, который хотите удалить:
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 и удаляют его.
Кроме того, оператор 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);