2013-10-25 44 views
25

Javaのfor-eachループを使用してNodeListを反復したいとします。私はforループとdo-whileループで動作しますが、for-eachでは動作しません。Javaでfor-eachを使用してNodeListを反復処理できますか?

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()); 
} 
+1

Iterableインターフェイスを実装していないため、NodeListにforeachループを使用することはできません。 nodeList.getLengthを使用してforループまたはwhileループを使用するオプションのみです。 http://docs.oracle.com/javase/7/docs/api/org/w3c/dom/NodeList.html –

+3

これは疑問な質問に接するものですが、私はw3cの使用を恥ずかしく思っていますJava標準ライブラリのもの。 IMOそれは熱い混乱で、XMLパースライブラリがはるかに優れています。 – Jazzepi

+0

+ Jazzepi古いトピックだけど、どのXML解析ライブラリをお勧めしますか?このw3cライブラリが単純で一般的なイテレータを提供していないという事実は、「詳細」ですが、(ライブラリの選択がこの点よりも複雑になる可能性がある場合でも) –

答えて

33

この問題の回避策は簡単です。ありがたいことに、一度だけ実装する必要があります。

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メソッドのstaticimportを追加した後:現在の場合

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

本当に便利です! :) – AweSIM

2

NodeListIterableを実装していないので、あなたが向上forループでそれを使用することはできません。

5

NodeListは単なるインターフェイスであるため、反復するためにNodeListIterableの両方を実装するクラスを作成できます。

5
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(); 
     } 
     }; 
    } 
    }; 
} 
+0

ここから:https://odoepner.wordpress.com/2012/07/13/turn-org-w3c-dom-nodelist-into-iterable/ –

1

DOM要素は、getElementsByTagName()などから作成されたNodeListを反復処理しながら(JavaScript経由で)削除され、要素は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); 
    } 
} 

このようにそれを使用します。sienceのための幸せ少しkotlinバージョンを追加 new IteratableNodeList(doc.getElementsByTagName(elementType)).forEach(...)

0

一つは、その後 nodeList.forEach { do_something_awesome() }でそれを使用することができます
fun NodeList.forEach(action: (Node) -> Unit) { 
    (0 until this.length) 
      .asSequence() 
      .map { this.item(it) } 
      .forEach { action(it) } 
} 

new IteratableNodeList(doc.getElementsByTagName(elementType)). stream().filter(...)

0

私はそれはあなたが(Iterator.remove用)(新しいIterableを作成するための)ラムダ式とデフォルトの方法を使用することにより、より簡潔@RayHulha's solutionを書くことができたJava-8ので
パーティーに遅れて、しかし...知っている:

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

、その後はこのようにそれを使用します。このような

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

または同等:

NodeList nodeList = ...; 
iterable(nodeList).forEach(node -> { 
    // .... 
}); 
関連する問題