2

I am using XPath in Java under Eclipse. I am writing a soap web service, the XML is using prefix and a namespace and I wanted to remove the prefix and only keep the namespace because I prefer a more readable looking XML document.

When I removed the prefixes from XML definition file and tried to query the XML with XPath I started to get null for every node in the XML!

My question is do I have to use a prefix if I want to use XPath? isn't just namespace is enough?

<myrequest
    xmlns="http://www.mywebsite.com/xml/webservice"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.mywebsite.com/xml/webservice Request.xsd">

   <state>     
       <value>demox</value>

this is how my XML starts and when I query this with XPath like XPath.selectSingleNode() I always receive null from the XPath for every node in it.

String myExpression = "myrequest/state/value";  
Document doc = new Document(requestXML);    
Element e = doc.getRootElement();
request.setMybase(getBase((org.jdom.Element) 
            XPath.selectSingleNode(doc,myExpression)));
Wayne
  • 59,728
  • 15
  • 131
  • 126
Spring
  • 11,333
  • 29
  • 116
  • 185

3 Answers3

3

This is the most FAQ on several XPath-related tags.

Whenever there is a default namespace in an XML document (as in your case), any element name in an XPath expression must be prefixed with a prefix that is bound to this default namespace. The binding is performed in the hosting language of XPath by typically "registering the default namespace" using a specific method/function.

The reason why this must be done:

Whenever there is an unprefixed name in an XPath expression, XPath treats this name as belonging to "no namespace".

Here is the normative text from the XPath 1.0 W3C Specification:

"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".

Therefore, evaluating /a/b/c on a document with a default namespace means: select all elements that are in "no namespace" and are named c, whose parent is an element named b that is in "no namespace", whose parent is the top element of the xml document and it is named a and is in "no namespace".

As all elements in the document are in its default namespace, and not in "no namespace", this XPath expression selects nothing.

Solution:

  1. In the API of your XPath engine, register the default namespace and bind to it a prefix (say "x").

  2. Now you can and must use this XPath expression: /x:myrequest/x:state/x:value instead of /myrequest/state/value

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
2

With XPath 1.0 to select elements in a namespace you need to use a prefix in your XPath expression to qualify element names. So even with the XML being cleaned up to not use a prefix but rather a default namespace declaration in your XPath expression you need to use a prefix bound to the default namespace you have (http://www.mywebsite.com/xml/webservice). How you bind a prefix to a namespace URI depends on the XPath API you use, I am not familiar with JDOM, check its API yourself on how to bind prefixes to namespace URIs.

[edit]The method is http://www.jdom.org/docs/apidocs/org/jdom/xpath/XPath.html#addNamespace%28java.lang.String,%20java.lang.String%29. so choose any prefix you like (e.g. "pf") and do addNamespace("pf", "http://www.mywebsite.com/xml/webservice"), then use that prefix in your XPath (e.g. "pf:myrequest/pf:state/pf:value").

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
1

My question is do I have to use a prefix if I want to use Xpath? isnt just namespace is enough?

You don't have to use a namespace prefix if you want to use XPath. Just the namespace is enough, but it makes your XPath much more concise, easier to read and maintain if you register and use a namespace prefix.

You could use an XPath with generic matches for elements (i.e. *) with predicate filters that specify the local-name() and namespace-uri() criteria, like this:

/*[local-name()='myrequest' 
   and namespace-uri()='http://www.mywebsite.com/xml/webservice'] 
    /*[local-name()='state' 
       and namespace-uri()='http://www.mywebsite.com/xml/webservice']
      /*[local-name()='value' 
         and namespace-uri()='http://www.mywebsite.com/xml/webservice']

However, it's much easier to register the namespace and use a namepace-prefix in your XPath.(e.g. /ws:myrequest/ws:state/ws:value )

Mads Hansen
  • 63,927
  • 12
  • 112
  • 147