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

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

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();
            }
        };
    }
}
48
задан Stefan Gehrig 4 November 2008 в 17:56
поделиться

10 ответов

В то время как 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
52
ответ дан Community 7 November 2019 в 22:06
поделиться

Существует способ удалить дочерний элемент через 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);
1
ответ дан 7 November 2019 в 12:06
поделиться

Идея о вспомогательных функциях взята из одного из комментариев к 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
ответ дан 7 November 2019 в 12:06
поделиться

Несмотря на то, что 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) ), потому что удаление элемента естественным образом уменьшает порядковый номер элементов, следующих за ним.

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

0
ответ дан 7 November 2019 в 12:06
поделиться

Для справки в будущем: удаление узлов с помощью 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"]');
2
ответ дан 7 November 2019 в 12:06
поделиться

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

//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 нужен только для того, чтобы убедиться, что целевой узел существует, прежде чем пытаться его переместить. Вы можете выбрать разные способы обработки или игнорирования этого случая.

10
ответ дан 7 November 2019 в 12:06
поделиться

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

foreach($doc->seg as &$seg) 
{
    if($seg['id'] == 'A12')
    {
        unset($seg);
    }
}
-2
ответ дан 7 November 2019 в 12:06
поделиться

Новая идея: 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]]);
}
1
ответ дан 7 November 2019 в 12:06
поделиться

Эта работа для меня:

$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();
5
ответ дан 7 November 2019 в 12:06
поделиться

Просто сбросьте узел:

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

23
ответ дан Álvaro González 7 November 2019 в 22:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: