removeChild() method is breaking for loop

人盡茶涼 提交于 2020-07-16 05:32:08

问题


I am using following code to remove multiple elements from XMl file.

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

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

But after removing one element it is coming out of loop. What is the issue.

Following is my XML file content.

<?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>

回答1:


I found the solution:

Let me explain what was the problem in detail.

NodeList removeNodeList = doc.getElementsByTagName("server1"); removeNodeList.getLength() will return 2 as there are 2 nodes with nodeName server1 then after executing rootElement.removeChild(removeNodeList.item(i)); and then checking for loop condition i.e. the value of i is 1 and removeNodeList.getLength() returns 1 as now only 1 node with nodeName server1 is remaining in DOM document and this condition was failing as 1 < 1 is false

So I followed the following approach:

Delete all the elements afterwards once the NodeList is no longer used.

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);
}



回答2:


Removing an element from the list decrements its size by 1 which breaks the iteration. You need to iterate the list backwards (from last to first node) as described in this question:

Removing DOM nodes when traversing a NodeList




回答3:


As already mentioned, removing an element reduces the size of the list but the counter is still increasing (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] 
--
--
--

The simplest solution, in my opinion, would be to remove i++ section in the loop.

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

Pointer stays on the same place. The list shifts by itself.

[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 
--
--
--
--



回答4:


To remove all elements contained in removeNodeList from xml-document:

Because the node is also removed from removeNodeList the next node to remove is on index 0 in removeNodeList until the list is empty (removeNodeList.getLength() == 0)

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

This works only if each node "server1" is a child of the node "start". If the xml would contain "server1" nodes as child of a node different from node rootElement ("start") a DOMException would be thrown because the node to remove is not a child of rootElement.

For example:

<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);
}

To remove elements conditionally, for example based on some attribute value:

Node will be removed only if method shouldRemoveNode() returns true. If false the node remains in document and removeNodeList, the next node to (possibly) remove is on index = current index + 1
notRemovedCnt is both the number of nodes not removed and also the index of the next element to check if it should be removed, as long until removeNodeList length and notRemoveCnt are equal which means no more elements to remove in list.

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


来源:https://stackoverflow.com/questions/21606204/removechild-method-is-breaking-for-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!