XML technologies
Namespaces with Java XPath
- Principle
- A problem
- The easiest solution to this
problem...
- Important note
- Example : querying the price of a book
from its BookReview
- Realisation for our RDF names &
marks example
-
-
To use XPath expressions with namespaces, the
XPath
object (created by an XPathFactory
)
must be equipped with a javax.xml.namespace.NamespaceContext
which will implement the relationship between the prefixes used in the present code and the URIs in the XML document.
There is no standard implementation for this
interface.
-
is to use an anonymous local class to implement the 3
methods required by the interface
public String getNamespaceURI(String prefix)
public Iterator getPrefixes(String val)
public String getPrefix(String uri)
Only the first one is useful, so that the two others may be summarily
defined as
public
Iterator getPrefixes(String val) {
return null;
}
public String
getPrefix(String uri) {
return null;
}
whereas the first one is conveniently written with a switch
statement on strings (from Java 7) :
public String
getNamespaceURI(String prefix) {
switch( prefix ){
case
"prefix-1" :
return
"URI-1";
case "prefix-2" :
return "
URI-2"
";
default :
return null;
}
}
-
the various strings prefix-1, prefix-2, etc in the
switch above are those that will be used to write the XPath
expressions in
the Java code,
they do not necessarily coincide with the namespace prefixes
that are found in the XML file.
(On the contrary, of course, the URIs must be the same).
Indeed, all the prefixes used here must be non-empty, also in
the case of a default namespace - in which case there will be a
difference between the Java code and the XML file.
As a consequence, the same code will apply equally to different
versions of the same XML document that differ only by the choice of
namespace prefixes, such as exnmsb.html
and exnmsh.html
from the Namespace course notes.
-
Intended execution
jfp$ java PriceQuery ../Namespaces/exnmsb.html
The price of "XML: A Primer" by Simon St. Laurent is : 31.98 (according
to a document with title "Book Review")
Same result for jfp$ java PriceQuery ../Namespaces/exnmsh.html
Here is the whole PriceQuery
class, so as to leave no uncertainty.
/* Querying the price of a book from its BookReview
With XPath and Namespaces
applies to both forms with and without a default
namespace*/
import org.xml.sax.InputSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.XMLConstants;
import java.util.Iterator;
public class PriceQuery {
public static String[] getInfo (InputSource src_ip)
throws Exception {
XPath xp =
XPathFactory.newInstance().newXPath();
NamespaceContext ctx = new
NamespaceContext() {
public String getNamespaceURI(String prefix) {
switch( prefix ){
case "bk" :
// was "xdc
" in the file
return "http://www.xml.com/books";
case "h" :
// was default namespace
return "http://www.w3.org/1999/xhtml";
default :
return null;
}
}
public Iterator getPrefixes(String val) {
return null;
}
public String getPrefix(String uri) {
return null;
}
}; // ctx
xp.setNamespaceContext(ctx);
String[] res = new String[4];
res[0] = (String) xp.evaluate("//bk:title",
src_ip, XPathConstants.STRING);
res[1] = (String) xp.evaluate("//bk:author",
src_ip, XPathConstants.STRING);
res[2] = (String) xp.evaluate("//bk:price",
src_ip, XPathConstants.STRING);
res[3] = (String) xp.evaluate("//h:title",
src_ip, XPathConstants.STRING);
return res;
}// getInfo
//----------------------------------------------------------------------------------------
public static void main(String[] args) throws
Exception {
InputSource src_ip = new
InputSource (args[0]);
String[] info = getInfo(src_ip);
System.out.println(
"The price of
\"" + info[0]
+ "\" by " +
info[1] + " is : " + info[2]
+ " (according
to a document with title \"" + info[3] +"\")"
);
}//main
}
-
Of course, if no default namespace is involved, a lazy programmer may
well reuse in his Java code
the prefixes he reads from a typical data file...
but this choice should not be misread as having any technical
significance!
NamespaceContext
ctx
= new NamespaceContext() {
public String
getNamespaceURI(String prefix) {
switch( prefix ){
case
"rdf" :
return
"http://www.w3.org/1999/02/22-rdf-syntax-ns#";
case "epita" :
return "http://epita/masters/international/";
default :
return null;
}
}
public
Iterator getPrefixes(String val) {
return null;
}
public String
getPrefix(String uri) {
return null;
}
}; // ctx
xp.setNamespaceContext(ctx);
See it at work : querying
for a mark from RDF Names&Marks data.