Java XPath processors ignoring namespace context

Viewed 88

I'm trying to query an XML document with a default namespace. I want to get the text content of a node in the default namespace.

I believe I've set things up correctly. However, I'm not getting results. I've created a NamespaceContext and passed it to the XPath processor. From the output it doesn't look like the context is being referenced. As such, the namespace-aware processor isn't finding the nodes in the default namespace.

When I try the query with a very ugly XPath expression that ignores namespaces, I get the expected result.

I've included an example program, the XML file being queried and the output.

Thanks.

The program

import java.io.IOException;
import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.List;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.xml.sax.InputSource;

public class Example {

    public static void main(String[] args) throws IOException, XPathExpressionException {
        List<String> pomLines = Files.readAllLines(Paths.get("example.xml"));
        String document = String.join(System.lineSeparator(), pomLines);

        // XPathFactory xpathFactory = new net.sf.saxon.xpath.XPathFactoryImpl();
        XPathFactory xpathFactory = XPathFactory.newInstance();
        XPath xpathNS = xpathFactory.newXPath();
        xpathNS.setNamespaceContext(new NamespaceContext() {

            @Override
            public String getNamespaceURI(String prefix) {
                System.out.println("*** getNamespaceURI, prefix='" + prefix + "'");
                if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) {
                    System.out.println("*** returning a URL");
                    return "http://maven.apache.org/POM/4.0.0";
                }

                System.out.println("*** returning NULL_NS_URI");
                return XMLConstants.NULL_NS_URI;
            }

            @Override
            public String getPrefix(String namespaceURI) {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            @Override
            public Iterator<String> getPrefixes(String namespaceURI) {
                throw new UnsupportedOperationException("Not supported yet.");
            }
        });

        XPathExpression xpathExpressionNS = xpathNS.compile("/project/name/text()");
        String artifactIdNS = xpathExpressionNS
                .evaluate(new InputSource(new StringReader(document)));
        System.out.println("artifactIdNS='" + artifactIdNS + "'");
        XPath xpath = xpathFactory.newXPath();
        XPathExpression xpathExpression = xpath
                .compile("/*[local-name()='project']/*[local-name()='name']/text()");
        String artifactId = xpathExpression.evaluate(new InputSource(new StringReader(document)));
        System.out.println("artifactId='" + artifactId + "'");

    }
}

The XML document

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.example</groupId>
        <artifactId>parent-test-repo</artifactId>
        <version>0.1.0</version>
    </parent>

    <artifactId>test-repo-1</artifactId>
    <name>test-repo-1</name>
    <version>1.1.0</version>
</project>

Actual output

~/temp% java Example
artifactIdNS=''
artifactId='test-repo-1'

I would have expected some lines with ***.

1 Answers

The xpath compiler does not invoke the namespace resolver if the xpath expression does not contains prefixes. This xpath would show the expected messages but not the expected value

XPathExpression xpathExpressionNS = xpathNS.compile("/:project/name/text()");

Result:

*** getNamespaceURI, prefix=''
*** returning a URL
artifactIdNS=''

A dummy prefix that maps the default namespace can be used to query the xml

            if ("mvn".equals(prefix)) {
                System.out.println("*** returning a URL");
                return "http://maven.apache.org/POM/4.0.0";
            }
// code

XPathExpression xpathExpressionNS = xpathNS.compile("/mvn:project/mvn:name/text()");

Result:

*** getNamespaceURI, prefix='mvn'
*** returning a URL
*** getNamespaceURI, prefix='mvn'
*** returning a URL
artifactIdNS='SO-68977962'
artifactId='SO-68977962'
Related