Метод removeChild() прерывает цикл

Я использую следующий код для удаления нескольких элементов из файла XML.

NodeList removeNodeList = doc.getElementsByTagName("server1");
Element rootElement = doc.getDocumentElement();

for (int i = 0; i < removeNodeList.getLength(); i++) {
    rootElement.removeChild(removeNodeList.item(i));
}

Но после удаления одного элемента он выходит из цикла. В чем проблема.

Ниже приведено содержимое моего файла XML.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<start>

    <category name="server1"/>
    <category name="server2"/>

    <server1 name="serverName1" value="serverValue"/>
    <server1 name="serverName1" value="serverValue"/>

    <server2 name="serverName2" value="serverValue"/>

</start>

person Vishrant    schedule 06.02.2014    source источник
comment
Я предполагаю, что NodeList является динамическим, и удаление элемента обновляет (уменьшает) длину, в результате чего ваше условие цикла оценивается только как истинное в первый раз. Это в первую очередь основано на том, как они работают в JavaScript, хотя у меня нет опыта работы с XML-документами в Java.   -  person Anthony Grist    schedule 06.02.2014
comment
Вы проверили несколько вещей, таких как длина removeNodeList. И каково содержимое xml после выполнения цикла for, удаляется ли он только один элемент?   -  person csn    schedule 06.02.2014
comment
@csn спасибо за ответ, я проверил размер NodeList, его 2 до того, как он уменьшился до 1.   -  person Vishrant    schedule 06.02.2014


Ответы (4)


Я нашел решение:

Позвольте мне подробно объяснить, в чем была проблема.

NodeList removeNodeList = doc.getElementsByTagName("server1"); removeNodeList.getLength() вернет 2, поскольку есть 2 узла с nodeName server1, затем после выполнения rootElement.removeChild(removeNodeList.item(i)); и последующей проверки for loop условия i.e. значение i равно 1, а removeNodeList.getLength() возвращает 1, так как теперь только 1 узел с nodeName server1 остался в DOM document, и это условие не выполнялось. как 1 < 1 is false

Поэтому я следовал следующему подходу:

Удалите все элементы после того, как NodeList больше не будет использоваться.

NodeList nodes = doc.getElementsByTagName(elementName);
Set<Element> targetElements = new HashSet<Element>();

for (int i = 0; i < nodes.getLength(); i++) {
    Element e = (Element)nodes.item(i);
    targetElements.add(e);
}
for (Element e: targetElements) {
    e.getParentNode().removeChild(e);
}
person Vishrant    schedule 06.02.2014

Удаление элемента из списка уменьшает его размер на 1, что прерывает итерацию. Вам нужно перебрать список в обратном порядке (от последнего узла к первому), как описано в этом вопросе:

Удаление узлов DOM при обходе NodeList

person Natix    schedule 06.02.2014

Как уже упоминалось, удаление элемента уменьшает размер списка, но счетчик все равно увеличивается (i++):

[element 1] <- Delete 
[element 2]
[element 3]
[element 4]
[element 5]

[element 2]  
[element 3] <- Delete
[element 4]
[element 5]
--

[element 2]  
[element 4] 
[element 5] <- Delete
--
--

[element 2]  
[element 4] 
--
--
--

На мой взгляд, самым простым решением было бы удалить из цикла раздел i++.

for (int i = 0; i < removeNodeList.getLength();) {
    rootElement.removeChild(removeNodeList.item(i));
}

Указатель остается на том же месте. Список сдвигается сам собой.

[element 1] <- Delete 
[element 2]
[element 3]
[element 4]
[element 5]

[element 2] <- Delete 
[element 3]
[element 4]
[element 5]
--

[element 3] <- Delete 
[element 4]
[element 5]
--
--

[element 4] <- Delete 
[element 5]
--
--
--

[element 5] <- Delete 
--
--
--
--
person Mikhaylo Plotnikov    schedule 29.12.2018

Чтобы удалить все элементы, содержащиеся в removeNodeList, из xml-документа:

Поскольку узел также удаляется из removeNodeList, следующий удаляемый узел находится в индексе 0 в removeNodeList, пока список не станет пустым (removeNodeList.getLength() == 0).

while(removeNodeList.getLength() > 0) {
    rootElement.removeChild(removeNodeList.item(0));
}

Это работает, только если каждый узел "server1" является дочерним по отношению к узлу "start". Если XML-файл будет содержать "server1" узлов в качестве дочерних узлов узла, отличного от узла rootElement ("начало"), будет выдано исключение DOMException, поскольку удаляемый узел не является дочерним элементом узла rootElement.

Например:

<category name="server1"/>
<category name="server2"/>

<server1 name="serverName1" value="serverValue"/>
<other>
   <server1 name="serverName1" value="serverValue"/>
</other>

<server2 name="serverName2" value="serverValue"/>

To handle this case get the parent node of the item to remove:

while(removeNodeList.getLength() > 0) {
    Node itemToRemove = removeNodeList.item(0);
    itemToRemove.getParentNode().removeChild(itemToRemove);
}

Чтобы удалить элементы условно, например, на основе значения какого-либо атрибута:

Узел будет удален, только если метод shouldRemoveNode() вернет true. Если false, узел остается в документе, а removeNodeList следующий узел, который (возможно) удаляется, находится в индексе = текущий индекс + 1
notRemovedCnt — это как количество не удаленных узлов, так и индекс следующего элемента, который нужно проверить, если он должен быть удален до тех пор, пока removeNodeList длина и notRemoveCnt не сравняются, что означает, что в списке больше нет элементов для удаления.

int notRemovedCnt = 0;
while(removeNodeList.getLength() > notRemovedCnt) {
    Node itemToRemove = removeNodeList.item(notRemovedCnt);
    if (shouldRemoveNode(itemToRemove)) {
        itemToRemove.getParentNode().removeChild(itemToRemove); 
    } else {
        notRemovedCnt++;
    }
}
person azb    schedule 16.06.2019