Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XPath returning null for "Node" when isNameSpaceAware and isValidating are "true"

Tags:

java

xpath

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>
like image 327
Pramod Avatar asked Jul 20 '11 10:07

Pramod


1 Answers

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.

like image 63
andyb Avatar answered Oct 21 '22 13:10

andyb