Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

generic foreach iteration of NamedNodeMap

In Java, looking at the NamedNodeMap interface, how do you iterate it with generics? It seems to use Node rather than String, but I'm not so sure how to use Node objects...

NamedNodeMap namedNodeMap = doc.getAttributes();
Map<String, String> stringMap = (Map<String, String>) namedNodeMap;
for (Map.Entry<String, String> entry : stringMap.entrySet()) {
  //key,value stuff here
}

Yes, I can see how to iterate without using generics and with a regular for loop, but I'd like to use the above ?idiom? for maps. Of course, the problem would appear to be that, despite the name, NamedNodeMap doesn't actually implement the Map interface! :(

Guess you just gotta bite the bullet here and do something like:

/*
 * Iterates through the node attribute map, else we need to specify specific 
 * attribute values to pull and they could be of an unknown type
 */
private void iterate(NamedNodeMap attributesList) {
    for (int j = 0; j < attributesList.getLength(); j++) {
        System.out.println("Attribute: "
                + attributesList.item(j).getNodeName() + " = "
                + attributesList.item(j).getNodeValue());
    }
}

there's nothing nicer?

like image 836
Thufir Avatar asked Nov 13 '10 06:11

Thufir


People also ask

How do you iterate over NamedNodeMap?

You can create your own Iterable wrapper for NamedNodeMap and then use it in a foreach loop. With a similar approach you could create an Iterable over Map. Entry<String, String> instances. This (and its sibling NodeListIterable ) is an absolute life saver when you have to work with DOM.

What is NamedNodeMap?

The NamedNodeMap interface represents a collection of Attr objects. Objects inside a NamedNodeMap are not in any particular order, unlike NodeList , although they may be accessed by an index as in an array.


3 Answers

You can create your own Iterable wrapper for NamedNodeMap and then use it in a foreach loop.

For example, this could be a simple implementation:

public final class NamedNodeMapIterable implements Iterable<Node> {

    private final NamedNodeMap namedNodeMap;

    private NamedNodeMapIterable(NamedNodeMap namedNodeMap) {
        this.namedNodeMap = namedNodeMap;
    }

    public static NamedNodeMapIterable of(NamedNodeMap namedNodeMap) {
        return new NamedNodeMapIterable(namedNodeMap);
    }

    private class NamedNodeMapIterator implements Iterator<Node> {

        private int nextIndex = 0;

        @Override
        public boolean hasNext() {
            return (namedNodeMap.getLength() > nextIndex);
        }
        @Override
        public Node next() {
            Node item = namedNodeMap.item(nextIndex);
            nextIndex = nextIndex + 1;
            return item;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }

    }

    @Override
    public Iterator<Node> iterator() {
        return new NamedNodeMapIterator();
    }
}

In this case, this would be the usage:

private void iterate(NamedNodeMap attributesList) {
    for (Node node : NamedNodeMapIterable.of(attributesList)) {
        System.out.println("Attribute: "
                + node.getNodeName() + " = " + node.getNodeValue());
    }
}

With a similar approach you could create an Iterable over Map.Entry<String, String> instances.

like image 161
Paolo Fulgoni Avatar answered Oct 11 '22 10:10

Paolo Fulgoni


I don't think there is a nicer way to use those APIs. (Update: OK - maybe https://stackoverflow.com/a/28626556/139985 counts as nice.)

Bear in mind that the W3C DOM Java APIs were specified before Java had generics or the new for syntax, or even the Iterator interface. Also bear in mind that the W3C DOM APIs for Java are actually the result of mapping an IDL specification to Java.

If you want nicer APIs for manipulating XML, etc in memory, maybe you should look at JDOM.

like image 21
Stephen C Avatar answered Oct 11 '22 10:10

Stephen C


As you can't cast NamedNodeMap to a Map, I suggest to loop using a classic for loop like that :

int numAttrs = namedNodeMap.getLength();
System.out.println("Attributes:");
for (int i = 0; i < numAttrs; i++){
   Attr attr = (Attr) pParameterNode.getAttributes().item(i);
   String attrName = attr.getNodeName();
   String attrValue = attr.getNodeValue();
   System.out.println("\t[" + attrName + "]=" + attrValue);
}
like image 39
Charles Follet Avatar answered Oct 11 '22 10:10

Charles Follet