6

Following is my XML file

<xyzevent xmlns="http://www.xyz.com/common/xyzevent/v1" xmlns:xsi="http://www.w3.org2001XMLSchema-instance">
<header>
 ----
</header>
<subscription xmlns="http://www.xyz.com/common/xyzevent/source/v1">
  <sender></sender>
  <receiver>
    <clientsubscription>
        <servicemap>nanna</servicemap>
    </clientsubscription>
  </receiver>
</subscription> 
</xyzevent>

When I budila org.w3c.dom.Document from this XML and applying XPathExperssion with expression

/xyzevent/subscription/receiver/clientsubscription/servicemap/text()

results empty string. What can be the issue with the expression?

Thank you

Kirill Polishchuk
  • 54,804
  • 11
  • 122
  • 125
Pokuri
  • 3,072
  • 8
  • 31
  • 55

4 Answers4

9

That's because your XML document uses a namespace. XPath is really annoying with namespaces. To confirm this, strip the two xmlns=http://.../v1 from the document and run your XPath expression agains the unnamespaced, unverifiable XML file. It'll match.

What's happening is that your XPath expression tries to select /xyzevent, when your document contains {http://.../v1}:xyzevent, which is not the same thing.

There are various ways around this problem. The proper way is to set up a NamespaceContext so you can use the prefix:localName notation in your XPath expression and have the prefixes be resolved to the correct URI. There's a short blurb about this in the xerces docs and some more elsewhere on StackOverflow. There's an extensive description at ibm.com.

Your NamespaceContext will contain two (or more) mappings:

{
    event => http://www.xyz.com/common/xyzevent/v1
    source => http://www.xyz.com/common/xyzevent/source/v1
}

Your XPath expression can then become /event:xyzevent/source:subscription/source:receiver/.../text().

As a nasty workaround, you can rewrite your xpath expression to select using the local-name() function:

/*[local-name()='xyzevent']/*[local-name()='subscription'/ ...

In this case, the expression matches any element whose local name is xyzevent, regardless of namespace URI.

Community
  • 1
  • 1
Barend
  • 17,296
  • 2
  • 61
  • 80
  • After removing xmlns=http://.../v1 this from both the places I am able to get the value. But can you explain me how to build NamespaceContext to get when we have both the namespace values in document... – Pokuri Aug 26 '11 at 13:51
  • @Pokuri Register two separate prefixes in your `NamespaceContext`, e.g. `event` and `source` with the proper URI's for each. Your XPath expression will become `/event:xyzevent/source:subscription/source:receiver/...` – Barend Aug 26 '11 at 13:54
  • But NamespaceContext expects one namespaceURI and one or more prefixes. Then how can I pass two NamespaceContext to XPath.setNamespaceContext(namespaceContext); method – Pokuri Aug 26 '11 at 14:06
  • The example on http://stackoverflow.com/questions/914013/namespacecontext-and-using-namespaces-with-xpath/920239#920239 covers this. – Barend Aug 26 '11 at 14:16
2

Your XML has default namespace: xmlns="http://www.xyz.com/common/xyzevent/v1", therefore you need to define it in your XML/XPath engine.

Or use this XPath:

/*[local-name() = 'xyzevent']
    /*[local-name() = 'subscription']
        /*[local-name() = 'receiver']
            /*[local-name() = 'clientsubscription']
                /*[local-name() = 'servicemap']
                    /text()
Kirill Polishchuk
  • 54,804
  • 11
  • 122
  • 125
1

xyzevent is your root element, so you just need to use "/subscription/receiver/clientsubscription/servicemap/text()".

Koraktor
  • 41,357
  • 10
  • 69
  • 99
1

I evaluated your expression in the following link:

http://www.whitebeam.org/library/guide/TechNotes/xpathtestbed.rhtm

And it seems, it selects "nanna".

suat
  • 4,239
  • 3
  • 28
  • 51