【问题标题】:removeChild() method is breaking for loopremoveChild() 方法正在中断 for 循环
【发布时间】:2023-03-31 23:45:01
【问题描述】:

我正在使用以下代码从 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>

【问题讨论】:

  • 我猜NodeList 是动态的,删除元素会更新(减少)长度,导致您的循环条件仅在第一次评估为真。这主要基于它们在 JavaScript 中的工作方式,不过,我没有在 Java 中处理 XML 文档的经验。
  • 你检查过一些东西吗 - removeNodeList 的长度。而执行for循环后xml的内容是什么,是不是只删除了一个元素?
  • @csn 感谢回复,我检查了NodeList 的大小,之前是2,然后减少到1。

标签: java xml jakarta-ee jaxp


【解决方案1】:

我找到了解决办法:

让我详细解释一下问题所在。

NodeList removeNodeList = doc.getElementsByTagName("server1"); removeNodeList.getLength() 将返回 2,因为有 2 个节点的节点名称为 server1,然后在执行 rootElement.removeChild(removeNodeList.item(i)); 并检查 for loop 条件 i.e. 之后,i 的值是 1removeNodeList.getLength() 返回 1因为现在只有 1 个节点名称为 server1 的节点保留在 DOM document 中,并且此条件失败为 1 &lt; 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);
}

【讨论】:

    【解决方案2】:

    从列表中删除一个元素会将其大小减 1,这会中断迭代。您需要按照此问题中的描述向后迭代列表(从最后一个节点到第一个节点):

    Removing DOM nodes when traversing a NodeList

    【讨论】:

      【解决方案3】:

      如前所述,删除元素会减小列表的大小,但计数器仍在增加 (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 
      --
      --
      --
      --
      

      【讨论】:

        【解决方案4】:

        从 xml-document 中删除 removeNodeList 中包含的所有元素:

        因为该节点也从 removeNodeList 中删除,所以下一个要删除的节点位于 removeNodeList 中的索引 0 上,直到列表为空 (removeNodeList.getLength() == 0)

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

        这仅在每个节点 "server1" 是节点 "start" 的子节点时才有效。 如果 xml 将包含 "server1" 节点作为不同于节点 rootElement(“start”)的节点的子节点,则会引发 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"/>
        

        要处理这种情况,请获取要删除的项目的父节点:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-22
          • 1970-01-01
          • 2019-02-01
          相关资源
          最近更新 更多