read example java xml dom

read - nodelist java example



EliminaciĆ³n de nodos DOM al atravesar un NodeList (7)

Estoy a punto de eliminar ciertos elementos en un documento XML, utilizando un código como el siguiente:

NodeList nodes = ...; for (int i = 0; i < nodes.getLength(); i++) { Element e = (Element)nodes.item(i); if (certain criteria involving Element e) { e.getParentNode().removeChild(e); } }

¿Interferirá esto con el recorrido adecuado de la Lista de nodos? ¿Alguna otra advertencia con este enfoque? Si esto es totalmente incorrecto, ¿cuál es la forma correcta de hacerlo?


Antiguo post, pero nada marcado como respuesta. Mi enfoque es iterar desde el final, es decir.

for (int i = nodes.getLength() - 1; i >= 0; i--) { // do processing, and then e.getParentNode().removeChild(e); }

Con esto, no tienes que preocuparte de que NodeList se acorte mientras eliminas.


Como ya se mencionó, la eliminación de un elemento reduce el tamaño de la lista pero el contador sigue aumentando (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] -- -- --

La solución más simple, en mi opinión, sería eliminar la sección i ++ del bucle y hacerlo cuando sea necesario cuando no se eliminó el elemento iterado.

NodeList nodes = ...; for (int i = 0; i < nodes.getLength();) { Element e = (Element)nodes.item(i); if (certain criteria involving Element e) { e.getParentNode().removeChild(e); } else { i++; } }

El puntero permanece en el mismo lugar cuando se eliminó el elemento iterado. La lista se desplaza sola.

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


De acuerdo con la especificación de DOM Level 3 Core,

el resultado de una llamada al método node.getElementsByTagName("...") será una referencia a un tipo NodeList " vivo ".

Los objetos NodeList y NamedNodeMap en el DOM están activos; es decir, los cambios en la estructura del documento subyacente se reflejan en todos los objetos relevantes de NodeList y NamedNodeMap. ... los cambios se reflejan automáticamente en el NodeList, sin más acción por parte del usuario.

1.1.1 El modelo de estructura DOM, párr. 2

JavaSE 7 cumple con la especificación DOM Nivel 3: implementa la interfaz NodeList vivo y la define como un tipo; define y expone el método getElementsByTagName en el elemento de la interfaz , que devuelve el tipo NodeList vivo .

Referencias

W3C - Modelo de objeto de documento (DOM) Especificación básica de nivel 3 - getElementsByTagName

JavaSE 7 - Elemento de interfaz

NodeList


De acuerdo con la especificación del DOM, el resultado de una llamada a node.getElementsByTagName ("...") se supone que está "en vivo", es decir, cualquier modificación realizada en el árbol DOM se reflejará en el objeto NodeList . Bueno, para las implementaciones conformes, eso es ...

Los objetos NodeList y NamedNodeMap en el DOM están activos; es decir, los cambios en la estructura del documento subyacente se reflejan en todos los objetos relevantes de NodeList y NamedNodeMap.

( Especificación DOM )

Entonces, cuando modifique la estructura de árbol, una implementación conforme cambiará el NodeList para reflejar estos cambios.



La eliminación de nodos mientras se realiza un bucle causará resultados no deseados, por ejemplo, resultados perdidos o duplicados. Esto ni siquiera es un problema con la sincronización y la seguridad de subprocesos, pero si los nodos son modificados por el propio bucle. La mayoría de los iteradores de Java lanzarán una excepción concurrente de modificación en tal caso, algo que NodeList no tiene en cuenta.

Se puede arreglar disminuyendo el tamaño de NodeList y disminuyendo el puntero del iteraror al mismo tiempo. Esta solución solo se puede utilizar si realizamos una acción de eliminación para cada iteración de bucle.

NodeList nodes = ...; for (int i = nodes.getLength() - 1; i >= 0; i--) { Element e = (Element)nodes.item(i); if (certain criteria involving Element e) { e.getParentNode().removeChild(e); } }


Por lo tanto, dado que la eliminación de nodos mientras atraviesa NodeList hará que se actualice NodeList para reflejar la nueva realidad, asumo que mis índices dejarán de ser válidos y esto no funcionará.

Por lo tanto, parece que la solución es hacer un seguimiento de los elementos que se eliminarán durante el recorrido, y luego eliminarlos todos, una vez que ya no se use el NodeList.

NodeList nodes = ...; Set<Element> targetElements = new HashSet<Element>(); for (int i = 0; i < nodes.getLength(); i++) { Element e = (Element)nodes.item(i); if (certain criteria involving Element e) { targetElements.add(e); } } for (Element e: targetElements) { e.getParentNode().removeChild(e); }