usar recorrer mejorado for como array java xml dom

recorrer - ¿Puedo iterar a través de una NodeList usando for-each en Java?



java foreach array (8)

Añadiendo la versión feliz de little kotlin para sience:

fun NodeList.forEach(action: (Node) -> Unit) { (0 until this.length) .asSequence() .map { this.item(it) } .forEach { action(it) } }

Uno puede usarlo con nodeList.forEach { do_something_awesome() }

Quiero iterar a través de NodeList usando un bucle for-each en Java. Lo tengo trabajando con un bucle for y un bucle do-while pero no para cada uno.

NodeList nList = dom.getElementsByTagName("year"); do { Element ele = (Element) nList.item(i); list.add(ele.getElementsByTagName("MonthId").item(0).getTextContent()); i++; } while (i < nList.getLength()); NodeList nList = dom.getElementsByTagName("year"); for (int i = 0; i < nList.getLength(); i++) { Element ele = (Element) nList.item(i); list.add(ele.getElementsByTagName("MonthId").item(0).getTextContent()); }


Como NodeList es solo una interfaz, podría crear una clase que implementaría tanto NodeList como Iterable , para poder iterar a través de ella.


La solución para este problema es directa, y afortunadamente debe implementarla solo una vez.

import java.util.*; import org.w3c.dom.*; public final class XmlUtil { private XmlUtil(){} public static List<Node> asList(NodeList n) { return n.getLength()==0? Collections.<Node>emptyList(): new NodeListWrapper(n); } static final class NodeListWrapper extends AbstractList<Node> implements RandomAccess { private final NodeList list; NodeListWrapper(NodeList l) { list=l; } public Node get(int index) { return list.item(index); } public int size() { return list.getLength(); } } }

Una vez que haya agregado esta clase de utilidad a su proyecto y haya agregado una import static para el método XmlUtil.asList a su código fuente, puede usarlo así:

for(Node n: asList(dom.getElementsByTagName("year"))) { … }


La solución validada es muy útil, pero aquí comparto una solución mejorada basada en la válida, esto te ayuda a iterar también, pero es fácil de usar y seguro:

public class XMLHelper { private XMLHelper() { } public static List<Node> getChildNodes(NodeList l) { List<Node> children = Collections.<Node>emptyList(); if (l != null && l.getLength() > 0) { if (l.item(0) != null && l.item(0).hasChildNodes()) { children = new NodeListWrapper(l.item(0).getChildNodes()); } } return children; } public static List<Node> getChildNodes(Node n) { List<Node> children = Collections.<Node>emptyList(); if (n != null && n.hasChildNodes()) { NodeList l = n.getChildNodes(); if (l != null && l.getLength() > 0) { children = new NodeListWrapper(l); } } return children; } private static final class NodeListWrapper extends AbstractList<Node> implements RandomAccess { private final NodeList list; NodeListWrapper(NodeList l) { list = l; } public Node get(int index) { return list.item(index); } public int size() { return list.getLength(); } }

}

Uso:

for (Node inner : XMLHelper.getChildNodes(node)) { ... }

Gracias @Holger.


Sé que es tarde para la fiesta, pero ...
Desde Java-8 puedes escribir la solución de @ RayHulha aún más concisamente usando la expresión lambda (para crear un nuevo Iterable ) y el método predeterminado (para Iterator.remove ):

public static Iterable<Node> iterable(final NodeList nodeList) { return () -> new Iterator<Node>() { private int index = 0; @Override public boolean hasNext() { return index < nodeList.getLength(); } @Override public Node next() { if (!hasNext()) throw new NoSuchElementException(); return nodeList.item(index++); } }; }

y luego usarlo así:

NodeList nodeList = ...; for (Node node : iterable(nodeList)) { // .... }

o equivalentemente así:

NodeList nodeList = ...; iterable(nodeList).forEach(node -> { // .... });


Si se elimina el elemento DOM actual (a través de JavaScript) mientras se itera una NodeList (creada a partir de getElementsByTagName () y tal vez otros), el elemento desaparecerá de NodeList. Esto hace que la iteración correcta de NodeList sea más complicada.

public class IteratableNodeList implements Iterable<Node> { final NodeList nodeList; public IteratableNodeList(final NodeList _nodeList) { nodeList = _nodeList; } @Override public Iterator<Node> iterator() { return new Iterator<Node>() { private int index = -1; private Node lastNode = null; private boolean isCurrentReplaced() { return lastNode != null && index < nodeList.getLength() && lastNode != nodeList.item(index); } @Override public boolean hasNext() { return index + 1 < nodeList.getLength() || isCurrentReplaced(); } @Override public Node next() { if (hasNext()) { if (isCurrentReplaced()) { // It got removed by a change in the DOM. lastNode = nodeList.item(index); } else { lastNode = nodeList.item(++index); } return lastNode; } else { throw new NoSuchElementException(); } } @Override public void remove() { throw new UnsupportedOperationException(); } }; } public Stream<Node> stream() { Spliterator<Node> spliterator = Spliterators.spliterator(iterator(), nodeList.getLength(), 0); return StreamSupport.stream(spliterator, false); } }

Entonces new IteratableNodeList(doc.getElementsByTagName(elementType)). stream().filter(...) así: new IteratableNodeList(doc.getElementsByTagName(elementType)). stream().filter(...) new IteratableNodeList(doc.getElementsByTagName(elementType)). stream().filter(...)

O bien: new IteratableNodeList(doc.getElementsByTagName(elementType)).forEach(...)


NodeList no implementa Iterable , por lo que no puede usarlo con el bucle for mejorado.


public static Iterable<Node> iterable(final NodeList n) { return new Iterable<Node>() { @Override public Iterator<Node> iterator() { return new Iterator<Node>() { int index = 0; @Override public boolean hasNext() { return index < n.getLength(); } @Override public Node next() { if (hasNext()) { return n.item(index++); } else { throw new NoSuchElementException(); } } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }; }