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.
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/"))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With