John Frost
John Frost

Reputation: 683

Java Dom parser reports wrong number of child nodes

I have the following xml file:

<?xml version="1.0" encoding="UTF-8"?>
<users>
<user id="0" firstname="John"/>
</users>

Then I'm trying to parse it with java, but getchildnodes reports wrong number of child nodes.

Java code:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(this.file);
document.getDocumentElement().normalize();
Element root = document.getDocumentElement();
NodeList nodes = root.getChildNodes();
System.out.println(nodes.getLength());

Result: 3

Also I'm getting NPEs for accessing the nodes attributes, so I'm guessing something's going horribly wrong.

Upvotes: 6

Views: 4792

Answers (4)

Patrick
Patrick

Reputation: 2702

I didn't notice any of the answers addressing your last note about NPEs when trying to access attributes.

Also I'm getting NPEs for accessing the nodes attributes, so I'm guessing something's going horribly wrong.

Since I've seen the following suggestion on a few sites, I assume it's a common way to access attributes:

String myPropValue = node.getAttributes().getNamedItem("myProp").getNodeValue();

which works fine if the nodes always contain a myProp attribute, but if it has no attributes, getAttributes will return null. Also, if there are attributes, but no myProp attribute, getNamedItem will return null.

I'm currently using

public static String getStrAttr(Node node, String key) {
    if (node.hasAttributes()) {
        Node item = node.getAttributes().getNamedItem(key);
        if (item != null) {
            return item.getNodeValue();
        }
    }

    return null;
}

public static int getIntAttr(Node node, String key) {
    if (node.hasAttributes()) {
        Node item = node.getAttributes().getNamedItem(key);
        if (item != null) {
            return Integer.parseInt(item.getNodeValue());
        }
    }

    return -1;
}

in a utility class, but your mileage may vary.

Upvotes: 0

bdoughan
bdoughan

Reputation: 149037

The child nodes consist of elements and text nodes for whitespace. You will want to check the node type before processing the attributes. You may also want to consider using the javax.xml.xpath APIs available in the JDK/JRE starting with Java SE 5.

Example 1

This example demonstrates how to issue an XPath statement against a DOM.

package forum11649396;

import java.io.StringReader;
import javax.xml.parsers.*;
import javax.xml.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.InputSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        String xml = "<?xml version='1.0' encoding='UTF-8'?><users><user id='0' firstname='John'/></users>";

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.parse(new InputSource(new StringReader(xml)));

        XPathFactory xpf = XPathFactory.newInstance();
        XPath xpath = xpf.newXPath();
        Element userElement = (Element) xpath.evaluate("/users/user", document, XPathConstants.NODE);
        System.out.println(userElement.getAttribute("id"));
        System.out.println(userElement.getAttribute("firstname"));
    }

}

Example 2

The following example demonstrates how to issue an XPath statement against an InputSource to get a DOM node. This saves you from having to parse the XML into a DOM yourself.

package forum11649396;

import java.io.StringReader;
import javax.xml.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.InputSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        String xml = "<?xml version='1.0' encoding='UTF-8'?><users><user id='0' firstname='John'/></users>";

        XPathFactory xpf = XPathFactory.newInstance();
        XPath xpath = xpf.newXPath();
        InputSource inputSource = new InputSource(new StringReader(xml));
        Element userElement = (Element) xpath.evaluate("/users/user", inputSource, XPathConstants.NODE);
        System.out.println(userElement.getAttribute("id"));
        System.out.println(userElement.getAttribute("firstname"));
    }

}

Upvotes: 4

mprivat
mprivat

Reputation: 21902

You have to make sure you account for the '\n' between the nodes, which count for text nodes. You can test for that using if(root.getNodeType() == Node.ELEMENT_NODE)

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(this.file);
        document.getDocumentElement().normalize();
        for(Node root = document.getFirstChild(); root != null; root = root.getNextSibling()) {
            if(root.getNodeType() == Node.ELEMENT_NODE) {
                NodeList nodes = root.getChildNodes();
                System.out.println(root.getNodeName() + " has "+nodes.getLength()+" children");
                for(int i=0; i<nodes.getLength(); i++) {
                    Node n = nodes.item(i);
                    System.out.println("\t"+n.getNodeName());
                }
            }
        }

Upvotes: 1

Gunther
Gunther

Reputation: 5256

There are three child nodes:

  • a text node containing a line break
  • an element node (tagged user)
  • a text node containing a line break

So when processing the child nodes, check for element nodes.

Upvotes: 3

Related Questions