RemoveChild removes the first child with this name, but skips the next one with the same name

Petar Yakov :

I have this node

<Record status="updated">
    <ID_Country>5</ID_Country>
    <ID_Currency>162</ID_Currency>
    <IsoCodeNumber>16  </IsoCodeNumber>
    <IsoCodeLetter version="old">AS  </IsoCodeLetter>
    <IsoCodeLetter version="new">ASAS  </IsoCodeLetter>
    <PostCode>    </PostCode>
    <CountryName>American Samoa  </CountryName>                        
    <isEuCountry>0</isEuCountry>
</Record>

I am trying to add this node in another XML-file and to look like this.

<Record>
     <ID_Country>5</ID_Country>
     <ID_Currency>162</ID_Currency>
     <IsoCodeNumber>16  </IsoCodeNumber>         
     <IsoCodeLetter>ASAS  </IsoCodeLetter>
     <PostCode>    </PostCode>
     <CountryName>American Samoa  </CountryName>                        
     <isEuCountry>0</isEuCountry>
</Record>

This is the code that I use

Node updatedNode = diffNode.cloneNode(true);
((Element) updatedNode).removeAttribute("status");    

for (int i = 0; i < updatedNode.getChildNodes().getLength(); i++)
{
    if (updatedNode.getChildNodes().item(i).getNodeType() == Node.ELEMENT_NODE)
    {                           
         Element e = (Element)updatedNode.getChildNodes().item(i);
         String string = e.getNodeName();

         if (e.hasAttribute("version") && e.getAttribute("version").equals("old"))
         {                 
              ((Element) updatedNode).removeChild((Node)e);                                
         }
         if(e.hasAttribute("version") && e.getAttribute("version").equals("new"))
         {              
               e.removeAttribute("version");
         }
    }
} 
productXML.adoptNode(updatedNode);
prodRoot.insertBefore(updatedNode, nextNode);

For some reason when the loop goes through the first IsoCodeLetter node and deletes it, it skips the next one and goes to PostCode, but the second IsoCodeLetter is still in the new Node, that I append to the XML-file and looks like this.

<Record>
      <ID_Country>5</ID_Country>
      <ID_Currency>162</ID_Currency>
      <IsoCodeNumber>16  </IsoCodeNumber>         
      <IsoCodeLetter version="new">ASAS  </IsoCodeLetter>
      <PostCode>    </PostCode>
      <CountryName>American Samoa  </CountryName>                        
      <isEuCountry>0</isEuCountry>
</Record>

Do you have any ideas why that happens and how to fix it? I am using DOMParser to write the XML file.

T.J. Crowder :

By removing a child from the element, you're removing it from the child nodes, meaning that the next child is now at the index of the child you removed. But your loop continues with the next index instead, skipping that child. For instance: Suppose you have:

0: Child A
1: Child B
2: Child C
3: Child D

For i == 0, let's say you don't remove Child A. Your loop adds one to i and you continue with i == 1. You do remove Child B. Now you have:

0: Child A
1: Child C
2: Child D

..but your loop adds one to i and now i == 2 and the next child you look at is Child D. You've never looked at Child C.

The usual solutions are:

  1. Loop backward, so the index is decreasing, and so it doesn't matter whether you remove a child at a given index; or

  2. Take a snapshot of the child nodes list before modifing the element, so that when you remove the child from the element, it doesn't get removed from your snapshot

Going backward is quite an easy change:

for (int i = updatedNode.getChildNodes().getLength() - 1; i >= 0; i--)

Side note: I'd probably also call getChildNodes just once and reuse the resulting NodeList object:

NodeList children = updatedNode.getChildNodes();
for (int i = children.getLength() - 1; i >= 0; i--)
    // ...and use `children`, not `updatedNode.getChildNodes()`, in the loop body...
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=131872&siteId=1