XML technologies

Namespaces with Java XPath

Jean-François Perrot

  1. Principle
    1. A problem
    2. The easiest solution to this problem...
    3. Important note

  2. Example : querying the price of a book from its BookReview

  3. Realisation for our RDF names & marks example

  1. Principle

    1. A problem

      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.
    2. The easiest solution to this problem...

      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;
                      }
                  }

    3. Important note

      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.

  2. Example : querying the price of a book from its BookReview

    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
    }



  3. Realisation for our RDF names & marks example

    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.