Rushil Paul
Rushil Paul

Reputation: 2048

How to make namespaces work with XPaths out of the box?

The online XPath tester works similar to my code below for the given XML and XPath (doesn't match anything): http://www.xpathtester.com/xpath

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

class test {

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

        XPathExpression expr = XPathFactory.newInstance().newXPath().compile(
            "/A[namespace-uri() = 'some-namespace']");  // This does not select anything, replacing A with * does 

        // This XPath selects as expected (in all parsers mentioned): /*[namespace-uri() = 'some-namespace']

        String xml = "<A xmlns=\"some-namespace\"> </A>";

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        Document doc = factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
        NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);

        System.out.println("Number of nodes selected: " + nodes.getLength());

        for (int i = 0; i < nodes.getLength(); i++) {
            System.out.println("Node name: " + nodes.item(i).getNodeName());        
        }
    }
}

Link to Ideone

The above code does not select anything regardless of whether the document factory is namespace aware.

Is that according to the XPath standard? Or an implementation nuance?

This resource mentions the below:

Indeed, when the XML document uses the default namespace, the XPath expression must use a prefix even though the target document does not.

To verify that, I changed the XPath to include a prefix like so: /p:A[namespace-uri() = 'some-namespace'] and added a namespace resolver that returned URI some-namespace for the prefix p, and that worked.


Questions:

1) Is there a way of making XPath expressions without prefixes work on documents that have default namespaces?

2) How does the [second XPath tester][3] work? (This tester doesn't conform to the standard)

Note: In my application, I cannot control the document and XPath that I receive. But both are guaranteed to be valid.

Upvotes: 1

Views: 133

Answers (1)

kjhughes
kjhughes

Reputation: 111716

Freeformatter.com XPath anomaly

For your sample XML,

<root>
    <A xmlns="some-namespace"> </A>
    <A xmlns="some-namespace2"> </A>
</root>

this XPath,

//A

should select nothing, yet on Freeformatter.com, it selects

<A xmlns="some-namespace2"> </A>

which is WRONG.

Therefore, full-stop, don't use Freeformatter.com. Don't try to work around this – simply don't use that service as it cannot be trusted to evaluate XPath in a conformant manner.


XPath namespaces in general

1) Is there a way of making XPath expressions without prefixes work on documents that have default namespaces?

You can

  1. Defeat namespaces via local-name() [not recommended]
  2. Honour namespaces via local-name() and namespace-uri() [ok, but verbose], or
  3. Define a namespace prefix via various mechanisms of the hosting language library [prefered but varies per XPath hosting system]

For further details, including examples of how to define a namespace prefix in many different hosting languages, see How does XPath deal with XML namespaces?


For a given, fixed XPath can selection disregard namespaces?

In my application, I cannot control the document and XPath that I receive (it is guaranteed to be valid). So I have to work with whatever XPath is sent to me. I'm guessing the answer to my question would be no then.?

If you're asking if there's a way to have //A to select a node from the above sample XML and still be conformant to XPath, then the answer is indeed no.

Upvotes: 1

Related Questions