4

Attempting to use XPath with an XML file that has a default namespace declared for the root node.

Example code:

    final SAXBuilder builder = new SAXBuilder();
    final Document document = builder.build(originalFile);

    final XPathFactory xFactory = XPathFactory.instance();

    final String expression = String.format("//section[@label='%s']/section/section", LABEL);
    final XPathExpression<Element> sectionExpression = xFactory.compile(expression, Filters.element());
    final List<Element> sections = sectionExpression.evaluate(document);

Sections is empty.

Snippet of the XML

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://www.stellent.com/sitestudio/Project/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.stellent.com/sitestudio/Project/ http://www.stellent.com/sitestudio/ss_project_schema.xsd">
    <section label="Index">
        <section label="xyz">
           <section label="child">
           ...
           </section>
        </section>
    </section>
</project>

Removing the xmlns="http://www.stellent.com/sitestudio/Project/" works but isn't a solution!

Why isn't XPath able to know about this default namespace? Also, why does it care?

Better yet, how can I generically fix this?

Thank you for any insight.

Thomas Beauvais
  • 1,546
  • 2
  • 16
  • 30
  • Note that XPath 2.0 does allow you to define a default namespace which applies to unprefixed element names in your XPath expression; but the default XPath API in JDOM2 uses XPath 1.0. If you want to use XPath 2.0 with JDOM2, you can do this via the Saxon XPath engine. – Michael Kay Jul 04 '14 at 14:22

1 Answers1

9

JDOM is doing the right thing. This is a FAQ, and not just for JDOM, but for XPath in general. The XPath specification is (somewhat) clear on this (I have bolded the relevant part):

A QName in the node test is expanded into an expanded-name using the namespace declarations from the expression context. This is the same way expansion is done for element type names in start and end-tags except that the default namespace declared with xmlns is not used: if the QName does not have a prefix, then the namespace URI is null (this is the same way attribute names are expanded). It is an error if the QName has a prefix for which there is no namespace declaration in the expression context.

From an XPath perspective, what this means is that the Namespace handling for rules in the XPath expression are not actually the same as the nodes in the XML. For all XPath expressions you need to define (duplicate) the namespace context for the expression, and the prefixes you use for the expression are actually completely independent to the ones used in the actual XML document.

You need to 'invent' a namespace prefix for your default namespace, and use that prefix in your expression (Here I have invented the namespace prefix ns):

final String expression = String.format("//ns:section[@label='%s']/ns:section/ns:section", LABEL);
final XPathExpression<Element> sectionExpression = xFactory.compile(expression, Filters.e, null,
         Namespace.getNamespace("ns", "http://www.stellent.com/sitestudio/Project/"))
rolfl
  • 17,539
  • 7
  • 42
  • 76
  • I still don't fully understand this.. but it works.. thanks! :) – Thomas Beauvais Jul 04 '14 at 13:28
  • Hey @ThomasBeauvais - I spend a lot of time in [The 2nd monitor stack exchange chat room](http://chat.stackexchange.com/rooms/8595/the-2nd-monitor). I will be happy to go through it in more detail. – rolfl Jul 04 '14 at 13:39
  • Is there a way to configure this so there is some kind of default namespace? So that no prefix is required? – ycomp Dec 24 '16 at 04:21
  • @ycomp - for XPath the answer is "No", and that's by design of XPath. It is an inconsistency between XML and XPath that has caught many people by surprise, but there's no way around it. It's part of the XPath specification. – rolfl Dec 24 '16 at 04:27
  • @rolfl thanks, at least I know that now.. I spent some time banging my head against the keyboard trying to figure it out. I just use a 1 letter prefix now – ycomp Dec 24 '16 at 13:59