8

I am getting a null node when I an trying to parse an XML file.

XPath xPath = XPathFactory.newInstance().newXPath();
    Node node = null;
    try {
        node = (Node) xPath.evaluate(
                "/mynode",
                doc,
                XPathConstants.NODE);

I am facing this issue only in case-
1. DocumentBuilderFactory- setNameSpaceAware is true
2. DocumentBuilderFactory- setValidating is true.

If these are set to false, then I am getting correct results. Can anyone help me on understanding what is the relation of setting these attributes to false? (I have checked this question, but it does not clear my doubt)

Here is the xml-

<?xml version="1.0" encoding="UTF-8"?>
<mynode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.example.com" xsi:schemaLocation="http://www.example.com example.xsd">
    <name>TargetName</name>
    <desc>desc goes here</desc>
    <pack>my.this</pack>
    <object>my.ExampleObject</object>
    <properties>
        <attrib>
            <name>id</name>
            <value>ZZZ</value>
        </attrib>
        <attrib>
            <name>ind</name>
            <value>X</value>
        </attrib>
    </properties>
    <children>
        <child>
            <name>childnodename</name>
            <desc>description goes here</desc>
            <invalues>
                <scope>ALL</scope>
            </invalues>
            <outvalues>
                <scope>ALL</scope>
            </outvalues>
            <akey>
                <aname>AAA</aname>
                <key></key>
            </akey>
            <msg>
                <success>code1</success>
                <failure>code2</failure>
            </msg>
        </child>
    </children>
</mynode>
Community
  • 1
  • 1
Pramod
  • 93
  • 1
  • 5

2 Answers2

4

The quickest fix is to not do setNamespaceAware(true); :-) However, if you want a namespace aware XPath then you have stumbled across a classic problem - XPath: Is there a way to set a default namespace for queries?, in that XPath does not support the concept of a default namespace.

So your XPath must use a namespace prefix in order for the query to find any nodes. However, you can set a NamespaceContext on the XPath instance to resolve the namespace prefix or default namespace to a URI. One way to do this, for example:

import java.util.*;
import java.io.ByteArrayInputStream;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.*;
import javax.xml.xpath.*;
import org.w3c.dom.*;

public class XmlParse {
    public static void main(String[] args) throws Exception {

        String xml =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
            "<mynode xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.example.com\" xsi:schemaLocation=\"http://www.example.com example.xsd\">" +
            "<name>TargetName</name>" +
            "</mynode>";
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder builder = dbf.newDocumentBuilder();
        Document doc = builder.parse(new ByteArrayInputStream(xml.getBytes()));

        final String nonameNamespace = doc.getFirstChild().getNamespaceURI();

        NamespaceContext ctx = new NamespaceContext() {
            public String getNamespaceURI(String prefix) {
                String uri = null;
                if (prefix.equals("n")) {
                    uri = nonameNamespace;
                }
                return uri;
            }

            @Override
            public Iterator getPrefixes(String val) {
                throw new IllegalAccessError("Not implemented!");
            }

            @Override
            public String getPrefix(String uri) {
                throw new IllegalAccessError("Not implemented!");
            }
        };

        XPath xPath = XPathFactory.newInstance().newXPath();
        xPath.setNamespaceContext(ctx);


        Node node = null;
        try {
            node = (Node) xPath.evaluate("/n:mynode/n:name", doc, XPathConstants.NODE);
            System.out.println(node.getNodeName());
            System.out.println(node.getFirstChild().getNodeValue());
        } catch (Exception e) {

        }
    }
}

So this will resolve the default namespace (xmlns) to http://www.example.com when a node with n prefix is encountered.

Community
  • 1
  • 1
andyb
  • 43,435
  • 12
  • 121
  • 150
0

XML is namespace-aware. Each XML element (and attribute) has an associated namespace; if not specified otherwise it's the empty (default) namespace.

In your case it is likely that the XML document you're trying to read uses namespaces, and your XPath query seems to only query the emtpy namespace. Therefore you don't get a result back. Make sure to use the proper namespace and it will work.

Lucero
  • 59,176
  • 9
  • 122
  • 152
  • Yes. My XML file uses a namespace. But I did not get your second point. Sorry, but I am a novice in XPath related stuff. Could you please elaborate how can this be done?- "your XPath query seems to only query the emtpy namespace. Therefore you don't get a result back. Make sure to use the proper namespace and it will work" – Pramod Jul 20 '11 at 10:48