问题
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 + 1notRemovedCnt
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