neves
neves

Reputation: 39443

How to use default namespace in dom4j selectNodes xpath expressions?

I'm using Dom4J to parse some Maven Pom files. When I use Pom files without a default namespace, everything works fine. For example:

Document pom = DocumentHelper.parseText(
                 "<project>" +
                 "   <groupId>xx.gov.xxx.sistema.xxx</groupId>" + 
                 "   <artifactId>sis-teste</artifactId>" + 
                 "   <packaging>war</packaging>" + 
                 "</project>");
//below works fine
String groupId = pom.selectSingleNode("/project/groupId").getText()

But if my Pom file defines a default namespace, it stops working:

Document pom = DocumentHelper.parseText(
                 "<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/maven-v4_0_0.xsd\">" +
                 "   <groupId>xx.gov.xxx.sistema.xxx</groupId>" + 
                 "   <artifactId>sis-teste</artifactId>" + 
                 "   <packaging>war</packaging>" + 
                 "</project>");
//NullPointerException!!!!!!!!!!!!!!!!!!!!
String groupId = pom.selectSingleNode("/project/groupId").getText()

The weird thing is that pom.selectSingleNode("/project") works fine.

How do I make my xpath query to work with the default namespace? I'd like to query just for "/project/groupId"and get the groupId node.

Upvotes: 3

Views: 1728

Answers (2)

neves
neves

Reputation: 39443

My hacky solution was just to remove the namespace of the pom file before creating the Dom object. Not really beautiful, but it works fine and life's go on.

Upvotes: 0

forty-two
forty-two

Reputation: 12817

Like so:

    Document pom = DocumentHelper.parseText(
            "<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/maven-v4_0_0.xsd\">" +
            "   <groupId>xx.gov.xxx.sistema.xxx</groupId>" +
            "   <artifactId>sis-teste</artifactId>" +
            "   <packaging>war</packaging>" +
            "</project>");
    Map<String, String> nsContext = new HashMap<>();
    nsContext.put("p", "http://maven.apache.org/POM/4.0.0");
    XPath xp = pom.createXPath("/p:project/p:groupId");
    xp.setNamespaceURIs(nsContext);
    String groupId = xp.selectSingleNode(pom).getText();
    System.out.println(groupId);

UPDATE

After a little closer look at the DOM4J code, this is possible, if you can tolerate setting a global namespace uri map:

    Map<String, String> nsContext = new HashMap<>();
    nsContext.put("p", "http://maven.apache.org/POM/4.0.0");
    DocumentFactory.getInstance().setXPathNamespaceURIs(nsContext);

    Document pom = DocumentHelper.parseText(
            "<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/maven-v4_0_0.xsd\">" +
            "   <groupId>xx.gov.xxx.sistema.xxx</groupId>" +
            "   <artifactId>sis-teste</artifactId>" +
            "   <packaging>war</packaging>" +
            "</project>");
    String groupId = pom.selectSingleNode("/p:project/p:groupId").getText();
    System.out.println(groupId);

A more localized solution would be to use a SAXReader and configure it with a specialized DocumentFactory, not the global one.

Upvotes: 4

Related Questions