У меня есть еще одно быстрое решение, реализуя это (что на самом деле является нечистым, но вы получаете идею):
public static void main(String[] args) {
System.out.println(StreamUtil.iterate(1, o -> o + 1).terminateOn(15)
.map(o -> o.toString()).collect(Collectors.joining(", ")));
}
static interface TerminatedStream<T> {
Stream<T> terminateOn(T e);
}
static class StreamUtil {
static <T> TerminatedStream<T> iterate(T seed, UnaryOperator<T> op) {
return new TerminatedStream<T>() {
public Stream<T> terminateOn(T e) {
Builder<T> builder = Stream.<T> builder().add(seed);
T current = seed;
while (!current.equals(e)) {
current = op.apply(current);
builder.add(current);
}
return builder.build();
}
};
}
}
В то время как SimpleXML обеспечивает способ удалить узлы XML, его возможности модификации несколько ограничены. Еще одно решение состоит в том, чтобы обратиться к использованию расширение DOM. dom_import_simplexml () поможет Вам с преобразованием Вашего SimpleXMLElement
в DOMElement
.
Просто некоторый пример кода (протестированный с PHP 5.2.5):
$data='<data>
<seg id="A1"/>
<seg id="A5"/>
<seg id="A12"/>
<seg id="A29"/>
<seg id="A30"/>
</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();
выводы
<?xml version="1.0"?>
<data><seg id="A1"/><seg id="A5"/><seg id="A29"/><seg id="A30"/></data>
Между прочим: выбор определенных узлов намного более прост при использовании XPath ( SimpleXMLElement-> xpath):
$segs=$doc->xpath('//seq[@id="A12"]');
if (count($segs)>=1) {
$seg=$segs[0];
}
// same deletion procedure as above
Существует способ удалить дочерний элемент через SimpleXml. Код ищет элемент, и ничего не делает. Иначе это добавляет элемент к строке. Это затем выписывает строку в файл. Также обратите внимание, что код сохраняет резервное копирование прежде, чем перезаписать исходный файл.
$username = $_GET['delete_account'];
echo "DELETING: ".$username;
$xml = simplexml_load_file("users.xml");
$str = "<?xml version=\"1.0\"?>
<users>";
foreach($xml->children() as $child){
if($child->getName() == "user") {
if($username == $child['name']) {
continue;
} else {
$str = $str.$child->asXML();
}
}
}
$str = $str."
</users>";
echo $str;
$xml->asXML("users_backup.xml");
$myFile = "users.xml";
$fh = fopen($myFile, 'w') or die("can't open file");
fwrite($fh, $str);
fclose($fh);
Идея о вспомогательных функциях взята из одного из комментариев к 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, поэтому не беспокойтесь о присвоении его какой-либо переменной.
Несмотря на то, что 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 ($ target)
), потому что удаление элемента естественным образом уменьшает порядковый номер элементов, следующих за ним.
По общему признанию, это немного обходной путь, но вроде работает нормально.
Для справки в будущем: удаление узлов с помощью SimpleXML иногда может быть проблемой, особенно если вы не знаете точную структуру документа. Вот почему я написал SimpleDOM , класс, который расширяет SimpleXMLElement, добавляя несколько удобных методов.
Например, deleteNodes () удалит все узлы, соответствующие выражению XPath. И если вы хотите удалить все узлы с атрибутом «id», равным «A5», все, что вам нужно сделать, это:
// don't forget to include SimpleDOM.php
include 'SimpleDOM.php';
// use simpledom_load_string() instead of simplexml_load_string()
$data = simpledom_load_string(
'<data>
<seg id="A1"/>
<seg id="A5"/>
<seg id="A12"/>
<seg id="A29"/>
<seg id="A30"/>
</data>'
);
// and there the magic happens
$data->deleteNodes('//seg[@id="A5"]');
Я считаю, что Стефан правильно ответил. Если вы хотите удалить только один узел (а не все совпадающие узлы), вот еще один пример:
//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 ... (last) могут быть заменены другим кодом в зависимости от того, откуда берутся ваши XML-данные и что вы хотите делать с выводом; именно участки между ними находят узел и удаляют его.
Кроме того, оператор if нужен только для того, чтобы убедиться, что целевой узел существует, прежде чем пытаться его переместить. Вы можете выбрать разные способы обработки или игнорирования этого случая.
Ваш первоначальный подход был правильным, но вы забыли одну мелочь о foreach. Он не работает с исходным массивом / объектом, но создает копию каждого элемента во время итерации, поэтому вы отключили копию. Используйте ссылку, подобную этой:
foreach($doc->seg as &$seg)
{
if($seg['id'] == 'A12')
{
unset($seg);
}
}
Новая идея: simple_xml
работает как массив.
Мы можем искать индексы «массива», который хотим удалить, а затем использовать функцию unset ()
для удаления индексов этого массива. Мой пример:
$pos=$this->xml->getXMLUser();
$i=0; $array_pos=array();
foreach($this->xml->doc->users->usr[$pos]->u_cfg_root->profiles->profile as $profile) {
if($profile->p_timestamp=='0') { $array_pos[]=$i; }
$i++;
}
//print_r($array_pos);
for($i=0;$i<count($array_pos);$i++) {
unset($this->xml->doc->users->usr[$pos]->u_cfg_root->profiles->profile[$array_pos[$i]]);
}
Эта работа для меня:
$data = '<data>
<seg id="A1"/>
<seg id="A5"/>
<seg id="A12"/>
<seg id="A29"/>
<seg id="A30"/></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();
Просто сбросьте узел:
$str = <<<STR
<a>
<b>
<c>
</c>
</b>
</a>
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
Этот код был взят от , Как удалить / удаляют узлы в SimpleXML.