NamespaceContext is the interface that JAXP
provides for establishing the namespace bindings used when an XPath
expression is evaluated. Unfortunately, as interfaces go, it leaves
a couple of things to be desired.
Try as hard as we may for perfection, the net result of our labors is an amazing variety of imperfectness. We are surprised at our own versatility in being able to fail in so many different ways.
In a namespace well-formed document, the namespace bindings in effect at any given point in the document are well-defined and self-evident. They are the sum of the declarations (and possible undeclarations) that have occurred in the document tree above the point in question (including the declarations on the current element, if the point happens to be an element).
So in the context of this document:
1<doc xmlns="http://example.org/ns/test" 2 xmlns:h="http://www.w3.org/1999/xhtml"> 3 <select xpath="//h:body"/> 4</doc>
The XPath expression in the
xpath attribute clearly
selects all the
elements in the context document that are named
body” and are in the
http://www.w3.org/1999/xhtml” namespace. (It doesn't
say anything about how the context document is established, but that's
One of the problems with using XPath expressions outside the XML context is that we lose this mechanism for determining the namespace bindings. When a QName appears on the side of a bus or in a string in a Java program, what does it mean? Consider:
1xpath.evaluate ("//t:p", doc);
What's the namespace binding for
The answer, in the case of
is in the
You can ask the
XPath object for a
NamespaceContext and subsequently
ask that context what's bound to the prefix in question:
1NamespaceContext nc = xpath.getNamespaceContext(); 2String uri = nc.getNamespaceURI("t");
So far, so good. Now suppose you want to use the
XPath API to find all the elements in a document that are named
body” and are in the
Easy, right? You (1) write the obvious XPath expression and
(2) cook up a
NamespaceContext that binds
the prefix you chose to the HTML namespace and (3) off you go.
So, remind me, how do you do that second part exactly?
Well, of course you can, but
it's no where near as easy as it should be. The
NamespaceContext interface doesn't have
an obvious constructor in the JAXP 1.3 API. (And it won't in 1.4
either, but I promise we'll clean this and a bunch of other usability
issues up in 1.5.)
To workaround this deficiency, I usually peek inside the
implementation and borrow a non-standard class or I implement the
NamespaceContext interface in some
private, inner class that does just barely enough work to get the job
done. The former is completely non-portable and the latter is a
The last time I blushed my way through this explanation with a developer, I decided I could at least implement the interface in some public place so that it could be reused.
I won't be surprised to learn that lots of folks, and lots of frameworks, have already done this. Maybe I could have saved myself a half-day's labor by pointing to one of those, but I also wanted to take NetBeans for a test drive.
The result of my labors is NamespaceContextHelper (and its JavaDoc) which you're welcome to borrow and reuse as you see fit. That package includes the sources, JavaDoc, unit tests, and NetBeans project files.
Speaking of unit tests, I've included a couple of extra tests to highlight common user-errors:
If you expect the XPath API to do coherent things, you must enable namespace support before parsing.
Parsing with namespaces disabled (which is unfortunately the default for the
DocumentBuilder) results in names that aren't in a namespace but may have colons. It's just too ugly to contemplate and it's bound to lead to anomalous XPath results.
Setting the default namespace in the
NamespaceContextdoes not allow you to change what unqualified names in your XPath expression match.
Unqualified names in an XPath (1.0) expression always match elements and attributes in no-namespace, irrespective of the binding in effect.
I've never been a big fan of IDEs, but I have to admit that NetBeans is a pretty nice environment for Java coding. It certainly simplified the construction and running of unit tests and offered lots of useful context-sensitive information. It's only serious flaw is that it isn't Emacs, but I'm not sure I can legitimately hold that against it.
The out-of-the box Emacs keybindings for NetBeans are ok, but they don't seem quite right. Maybe I'm being mislead by my own local Emacs customizations. It's not all my fault though because, for example, “Alt-W”, which should copy the current selection into the clipboard, sometimes drops down the “Window” menu. Bleh. I'm sure I can tweak the bindings, but I haven't tried yet.
Honestly, nice as NetBeans is, I am really, really reluctant to abandon Emacs for anything. I think I'll get a recent version of JDEE and see if I can get it to help me sufficiently to keep me as productive as NetBeans clearly could.
It's not obvious that it's going to be practical, NetBeans is that good.