NamespaceContext and using namespaces with XPath
Solution 1
It is possible to get a NamespaceContext instance without writing your own class. Its class-use page shows you can get one using the javax.xml.stream package.
String ctxtTemplate = "<data xmlns=\"http://base\" xmlns:foo=\"http://foo\" />";
NamespaceContext nsContext = null;
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLEventReader evtReader = factory
.createXMLEventReader(new StringReader(ctxtTemplate));
while (evtReader.hasNext()) {
XMLEvent event = evtReader.nextEvent();
if (event.isStartElement()) {
nsContext = ((StartElement) event)
.getNamespaceContext();
break;
}
}
System.out.println(nsContext.getNamespaceURI(""));
System.out.println(nsContext.getNamespaceURI("foo"));
System.out.println(nsContext
.getNamespaceURI(XMLConstants.XMLNS_ATTRIBUTE));
System.out.println(nsContext
.getNamespaceURI(XMLConstants.XML_NS_PREFIX));
Forgoing prefixes altogether is likely to lead to ambiguous expressions - if you want to drop namespace prefixes, you'd need to change the document format. Creating a context from a document doesn't necessarily make sense. The prefixes have to match the ones used in the XPath expression, not the ones in any document, as in this code:
String xml = "<data xmlns=\"http://base\" xmlns:foo=\"http://foo\" >"
+ "<foo:value>"
+ "hello"
+ "</foo:value>"
+ "</data>";
String expression = "/stack:data/overflow:value";
class BaseFooContext implements NamespaceContext {
@Override
public String getNamespaceURI(String prefix) {
if ("stack".equals(prefix))
return "http://base";
if ("overflow".equals(prefix))
return "http://foo";
throw new IllegalArgumentException(prefix);
}
@Override
public String getPrefix(String namespaceURI) {
throw new UnsupportedOperationException();
}
@Override
public Iterator<String> getPrefixes(
String namespaceURI) {
throw new UnsupportedOperationException();
}
}
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
xpath.setNamespaceContext(new BaseFooContext());
String value = xpath.evaluate(expression,
new InputSource(new StringReader(xml)));
System.out.println(value);
Neither the implementation returned by the StAX API nor the one above implement the full class/method contracts as defined in the doc. You can get a full, map-based implementation here.
Solution 2
I've just been working through using xpath and NamespaceContexts myself. I came across a good treatment of the issue on developerworks.
Solution 3
I found a convenient implementation in "Apache WebServices Common Utilities" called NamespaceContextImpl.
You can use the following maven dependency to obtain this class:
<dependency>
<groupId>org.apache.ws.commons</groupId>
<artifactId>ws-commons-util</artifactId>
<version>1.0.1</version>
</dependency>
I've use it in the following manner (I know its built for sax, but after reading the code, its o.k):
NamespaceContextImpl nsContext = new NamespaceContextImpl();
nsContext.startPrefixMapping("foo", "my.name.space.com");
You don't need to called endPrefixMapping.
Solution 4
If you are using the Spring framework you can reuse their NamespaceContext implementation
org.springframework.util.xml.SimpleNamespaceContext
This is a similar answer like the one from Asaf Mesika. So it doesn't give you automatic a NamespaceContext based on your document. You have to construct it yourself. Still it helps you because it at least gives you an implementation to starts with.
When we faced a similar problem, Both the spring SimpleNamespaceContext
and the "Apache WebServices Common Utilities" worked. We wanted to avoid to the addition jar dependency on Apache WebServices Common Utilities and used the Spring one, because our application is Spring based.
Jherico
Java, C++, OpenGL, VR Author of *[Oculus Rift in Action][1]*
Updated on July 09, 2022Comments
-
Jherico almost 2 years
Resolving an xpath that includes namespaces in Java appears to require the use of a
NamespaceContext
object, mapping prefixes to namespace urls and vice versa. However, I can find no mechanism for getting aNamespaceContext
other than implementing it myself. This seems counter-intuitive.The question: Is there any easy way to acquire a
NamespaceContext
from a document, or to create one, or failing that, to forgo prefixes altogether and specify the xpath with fully qualified names?-
Admin over 13 yearsThere is some basic missundertood from your question: you can get all the namespaces URI from an XML with XPath (there are answers for this in SO), but you can't simple populate a prefix-namespace URI binding class because for each node it might be a different bound. When selecting nodes you should know in advance the names of your desired elements and that includes their namespace URI
-
-
Jherico about 15 yearsThis sort of confirms my fears. I can't have have a NamespaceContext that does arbitrary mapping unless I implement the class myself. But the XML streams example at least gives me a path for creating a NamespaceContext from a static function that builds a micro document as you do in the first example. I'll try that.
-
McDowell about 15 yearsYes, it is a bit of a pain that there is no default implementation.
-
Jherico about 15 yearsI submit that that is stupid. How many other java interfaces which are used in public APIs have no publicly available concrete implementations or factories?
-
Meitham almost 13 yearsyou're still hardcoding the namespaces!
-
Eric Bréchemier almost 12 yearsExactly what I was looking for. The javadoc: ws.apache.org/commons/util/apidocs/index.html
-
Asaf Mesika almost 12 yearsMeitham - I don't any way that avoids hardcoding the name space. You are bootstrapping the XPath search by creating this namespace context, initializing it by mapping a short prefix to each namespace you know contained in the document, and then when searching by XPath you use those prefixes when defining the path.
-
kevinarpe about 11 yearsAfter reading this essay from IBM, I can understand why there isn't a default implementation. There are a variety of ways to solve this problem. That said: Why doesn't an open source group, like Apache, create a library of different implementations?