Javaでfor-eachループを使用してNodeList
を反復処理したい。 forループとdo-whileループで動作しますが、for-eachでは動作しません。
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());
}
この問題の回避策は簡単であり、ありがたいことに一度だけ実装する必要があります。
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();
}
}
}
このユーティリティクラスをプロジェクトに追加し、XmlUtil.asList
メソッドのstatic
import
をソースコードに追加したら、次のように使用できます。
for(Node n: asList(dom.getElementsByTagName("year"))) {
…
}
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();
}
};
}
};
}
NodeList
は単なるインターフェイスであるため、NodeList
とIterable
の両方を実装するクラスを作成して、繰り返し処理することができます。
科学用のハッピーリトルコトリンバージョンの追加:
_fun NodeList.forEach(action: (Node) -> Unit) {
(0 until this.length)
.asSequence()
.map { this.item(it) }
.forEach { action(it) }
}
_
nodeList.forEach { do_something_awesome() }
で使用できます
NodeList
はIterable
を実装しないため、拡張されたfor
ループでは使用できません。
検証済みのソリューションは非常に便利ですが、ここでは有効なソリューションに基づいて改善されたソリューションを共有します。これにより、繰り返しもできますが、使いやすく安全です。
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();
}
}
}
使用法:
for (Node inner : XMLHelper.getChildNodes(node)) { ... }
@Holgerに感謝します。
NodeList(getElementsByTagName()などから作成)の反復中に(JavaScriptを介して)現在のDOM要素が削除されると、要素はNodeListから消えます。これにより、NodeListの正しい反復がよりトリッキーになります。
_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);
}
}
_
次に、次のように使用します:new IteratableNodeList(doc.getElementsByTagName(elementType)). stream().filter(...)
または:new IteratableNodeList(doc.getElementsByTagName(elementType)).forEach(...)
org.Apache.commons.collections4.iterators.NodeListIterator
およびcom.Sun.xml.internal.ws.util.xml.NodeListIterator
。