XPath with namespaces in Java

XPath with namespaces in Java

XPath is the expression language operating on an XML tree, used from XSLT. It can also be used stand-alone, such as from a Java application. There is also a standard API, called JAXP, for Java. So, everything is nice and dandy, and the post should stop here! Well... for real XML documents, using namespaces, it is not this easy. That is why you need this post!

If you do not care about lengthy posts, but just want the bare facts, go to the end of this post :-)

Let us first look at a simple example where the XML documents do not have namespaces. Say we have this XML document with some orders:

XML:
  1. <? xml version="1.0"?>
  2. <Sales>
  3. <Customer name="CostCo, Inc.">
  4. <Order price="2000">
  5.   <Description>A tiny web site </Description>
  6. </Order>
  7. <Order price="12000">
  8.   <Description>Database configuration </Description>
  9. </Order>
  10. </Customer>
  11. <Customer name="BigBuyer, Inc.">
  12. <Order price="30000">
  13.   <Description>A J2EE system for lawyers </Description>
  14. </Order>
  15. </Customer>
  16. </Sales>

 

A quick XPath to get all big orders from all customers is:

XPATH:
//Customer/Order[@price> 10000]

 

Sun provides us with an API, JAXP, for accessing and manipulating XML documents, including using XPath. So, let us see exactly what we need to do to use XPath.

If you use Java 5.0, then you already have JAXP at your fingertips. Version 1.3 of JAXP, to be exact. Having an API is no fun without a corresponding implementation of it, though. Fortunately, J2SE comes with a reference implementation, based on Xerces and Xalan. This in contrast to Java 1.4, which used Crimson. There are some crucial differences between these versions, but I will leave that discussion - it is just too painful ;-)

Using Java 5.0, a complete application, which you can just compile and run, follows:

JAVA:
  1. package com. davber. test;
  2.  
  3. import javax.xml.parsers.*;
  4. import javax.xml.xpath.*;
  5.  
  6. import org.w3c.dom.Document;
  7.  
  8. public class App
  9. {
  10.     public static void main ( String [ ] args )
  11.     {
  12.         try {
  13.             // First, the XML document
  14.            
  15.             String xmlStr =
  16.                 "<?xml version=/"1.0/" ?>/n" +
  17.                 "<Sales xmlns=/"http://www.davber.com/sales-format/">/n" +
  18.                 "<Customer name=/"CostCo, Inc./">/n" +
  19.                 "<ord:Order xmlns:ord=/"http://www.davber.com/order-format/" price=/"12000/">/n" +
  20.                 "<ord:Description>A bunch of stuff" +
  21.                 "</ord:Description>/n" +
  22.                 "</ord:Order>/n" +
  23.                 "</Customer>/n" +
  24.                 "</Sales>/n";
  25.             DocumentBuilderFactory xmlFact =
  26.                 DocumentBuilderFactory. newInstance ( );
  27.             xmlFact. setNamespaceAware ( true );
  28.             DocumentBuilder builder = xmlFact.
  29.                 newDocumentBuilder ( );
  30.             Document doc = builder. parse (
  31.                     new java. io. ByteArrayInputStream (
  32.                             xmlStr. getBytes ( ) ) );
  33.            
  34.             // Now the XPath expression
  35.            
  36.             String xpathStr =
  37.                 "//Customer/Order[@price> 6000]";
  38.             XPathFactory xpathFact =
  39.                 XPathFactory. newInstance ( );
  40.             XPath xpath = xpathFact. newXPath ( );
  41.             String result = xpath. evaluate (xpathStr, doc );
  42.             System. out. println ( "XPath result is /"" +
  43.                     result + "/"" );
  44.         }
  45.         catch ( Exception ex ) {
  46.             ex. printStackTrace ( );
  47.         }
  48.     }
  49. }

 

Compile and run it now. You will see XPath result is "A bunch of stuff" i.e., it matched one order.

So, let us get down to business and add some namespaces to the input document.

XML:
  1. <? xml version="1.0"?>
  2. <Sales xmlns="http://www.davber.com/sales-format">
  3. <Customer name="CostCo, Inc.">
  4. <ord:Order xmlns:ord="http://www.davber.com/order-format" price="2000">
  5.   <ord:Description>A tiny web site </ord:Description>
  6. </ord:Order>
  7. <ord:Order price="12000">
  8.   <ord:Description>Database configuration </ord:Description>
  9. </ord:Order>
  10. </Customer>
  11. <Customer name="BigBuyer, Inc.">
  12. <ord:Order price="30000">
  13.   <ord:Description>A J2EE system for lawyers </ord:Description>
  14. </ord:Order>
  15. </Customer>
  16. </Sales>

 

So we have two namespaces, where one is the the default namespace, i.e., added to all local names without an explicit prefix. This is important to understand, since that means they will not match names without namespace in XPath. So, even for the default namespace, we need to explicitly add a namespace to the XPath! Let us try without namespaces, i.e., use the exact same code as before but with the namespace-annotated variant of the input XML. You will get this intriguing output: XPath result is ""

Yep, nothing! Your non-namespaced XPath did not match the now namespaced input. No real surprise here. What might come as a surprise is that we cannot even use local names in the XPath expression for names in the default namespace. Try with the XPath

XPATH:
/Sales/Customer/@name

which should give us the concattenation of all the customer names. Replace the XPath of the application and recompile and run. You will again see this output: XPath result is "" i.e., no match!

So, as soon as we have namespaces in the XML document, default or not, we need to use namespaces in the XPath. We will later see how can circumvent this requirement, but let us now take it as a gospel :-)

How do we adorn the XPath with namespaces? Well, just as done in the XML document, there has to be a mapping between prefixes and URIs. The problem in the stand-alone XPath case - as opposed to when it is used within an XSLT document - is that there is no place to do it in the XPath "document" itself.

Can we not just use the same prefixes as the XML document?

No, the matching procedure only cares about the (mapped) URIs and could not care less about the specific prefix we chose. If you do not believe me, try with the prefixed XPath

XPATH:
//ord:Order/ord:Description

 

Believe me now?

Ok, so how do we create and provide that mapping in JAXP?

There is this interface javax.xml.namespace.NamespaceContext which declares three methods, whereof we only care about one of them, which maps namespace prefix to URI. The other two are not used by the standard XPath executing implementations (AFAIK.) The complete application with an implementation of that interface corresponding to our test XML input follows.

JAVA:
  1. package com. davber. test;
  2.  
  3. import java.util.Iterator;
  4. import java.util.List;
  5.  
  6. import javax.xml.namespace.NamespaceContext;
  7. import javax.xml.parsers.*;
  8. import javax.xml.xpath.*;
  9.  
  10. import org.w3c.dom.Document;
  11.  
  12. public class App
  13. {
  14.     public static void main ( String [ ] args )
  15.     {
  16.         try {
  17.             // First, the XML document
  18.            
  19.             String xmlStr =
  20.                 "<?xml version=/"1.0/" ?>/n" +
  21.                 "<Sales xmlns=/"http://www.davber.com/sales-format/">/n" +
  22.                 "<Customer name=/"CostCo, Inc./">/n" +
  23.                 "<ord:Order xmlns:ord=/"http://www.davber.com/order-format/" price=/"12000/">/n" +
  24.                 "<ord:Description>A bunch of stuff" +
  25.                 "</ord:Description>/n" +
  26.                 "</ord:Order>/n" +
  27.                 "</Customer>/n" +
  28.                 "</Sales>/n";
  29.             DocumentBuilderFactory xmlFact =
  30.                 DocumentBuilderFactory. newInstance ( );
  31.             xmlFact. setNamespaceAware ( true );
  32.             DocumentBuilder builder = xmlFact.
  33.                 newDocumentBuilder ( );
  34.             Document doc = builder. parse (
  35.                     new java. io. ByteArrayInputStream (
  36.                             xmlStr. getBytes ( ) ) );
  37.            
  38.             // We map the prefixes to URIs
  39.            
  40.             NamespaceContext ctx = new NamespaceContext ( ) {
  41.                 public String getNamespaceURI ( String prefix ) {
  42.                     String uri;
  43.                     if (prefix. equals ( "ns1" ) )
  44.                         uri = "http://www.davber.com/order-format";
  45.                     else if (prefix. equals ( "ns2" ) )
  46.                         uri = "http://www.davber.com/sales-format";
  47.                     else
  48.                         uri = null;
  49.                     return uri;
  50.                 }
  51.                
  52.                 // Dummy implementation - not used!
  53.                 public Iterator getPrefixes ( String val ) {
  54.                     return null;
  55.                 }
  56.                
  57.                 // Dummy implemenation - not used!
  58.                 public String getPrefix ( String uri ) {
  59.                     return null;
  60.                 }
  61.             };
  62.            
  63.             // Now the XPath expression
  64.            
  65.             String xpathStr =
  66.                 "//ns1:Order/ns1:Description";
  67.             XPathFactory xpathFact =
  68.                 XPathFactory. newInstance ( );
  69.             XPath xpath = xpathFact. newXPath ( );
  70.             xpath. setNamespaceContext (ctx );
  71.             String result = xpath. evaluate (xpathStr, doc );
  72.             System. out. println ( "XPath result is /"" +
  73.                     result + "/"" );
  74.         }
  75.         catch ( Exception ex ) {
  76.             ex. printStackTrace ( );
  77.         }
  78.     }
  79. }

 

Now the XPath matches. Note that I did not use the same prefix as the XML document, since the choice of prefix is arbitrary. In order to match against the default namespace in the XML document, we need to use the prefix ns2 then, as in the following XPath expression:

XPATH:
/ns2:Sales/ns2:Customer/@name

 

Try it and you will get: XPath result is "CostCo, Inc."

If you think it is a PITA to explicitly map those prefixes to namespaces you have two choices:

  1. extract the mapping from the XML document itself
  2. disable the namespace awareness of the XML document

Let us look at the first alternative. Unfortunately (?), there is no such facility in the JAXP API, so we have to diverge into implementation specificity. Xalan contains an interface PrefixResolver (at least in version 2.7.0) which is very similar to the aforementioned JAXP interface. Unfortunately, "similar" is not the same as "compatible with", so we need to wrap the PrefixResolver as NamespaceContext to use it with the JAXP API.

Why do we want to use this non-compatible prefix resolver interface in the first case? Because of a convenient implementation called PrefixResolverDefault. Instances suck their namespace mapping out of a living DOM node, as in

JAVA:
  1. PrefixResolver resolver = new PrefixResolverDefault (doc. getDocumentElement ( ) );

 

Making that prefix resolver compatible with JAXP is not that hard:

JAVA:
  1. NamespaceContext ctx = new NamespaceContext ( ) {
  2.     public String getNamespaceURI ( String prefix ) {
  3.       return resolver. getNamespaceForPrefix (prefix );
  4.     }

 

The other two methods have their old dummy implementation. Of course you need to make the prefix resolver final before using it from the instance of that anonymous class, but you already know that...

The complete code with this Xalan tool follows.

JAVA:
  1. package com. davber. test;
  2.  
  3. import java.util.Iterator;
  4.  
  5. import javax.xml.namespace.NamespaceContext;
  6. import javax.xml.parsers.*;
  7. import javax.xml.xpath.*;
  8.  
  9. import org.w3c.dom.Document;
  10.  
  11. import com.sun.org.apache.xml.internal.utils.PrefixResolver;
  12. import com.sun.org.apache.xml.internal.utils.PrefixResolverDefault;
  13.  
  14. public class App
  15. {
  16.     public static void main ( String [ ] args )
  17.     {
  18.         try {
  19.             // First, the XML document
  20.            
  21.             String xmlStr =
  22.                 "<?xml version=/"1.0/" ?>/n" +
  23.                 "<Sales xmlns=/"http://www.davber.com/sales-format/">/n" +
  24.                 "<Customer name=/"CostCo, Inc./">/n" +
  25.                 "<ord:Order xmlns:ord=/"http://www.davber.com/order-format/" " +
  26.                 "price=/"12000/">/n" +
  27.                 "<ord:Description>A bunch of stuff" +
  28.                 "</ord:Description>/n" +
  29.                 "</ord:Order>/n" +
  30.                 "</Customer>/n" +
  31.                 "</Sales>/n";
  32.             DocumentBuilderFactory xmlFact =
  33.                 DocumentBuilderFactory. newInstance ( );
  34.             xmlFact. setNamespaceAware ( true );
  35.             DocumentBuilder builder = xmlFact.
  36.                 newDocumentBuilder ( );
  37.             Document doc = builder. parse (
  38.                     new java. io. ByteArrayInputStream (
  39.                             xmlStr. getBytes ( ) ) );
  40.            
  41.             // We map the prefixes to URIs, using Xalan's
  42.             // document-extracting mapping tool and wrapping
  43.             // it in a nice JAXP shell
  44.            
  45.             final PrefixResolver resolver =
  46.                 new PrefixResolverDefault (doc. getDocumentElement ( ) );
  47.             NamespaceContext ctx = new NamespaceContext ( ) {
  48.                 public String getNamespaceURI ( String prefix ) {
  49.                     return resolver. getNamespaceForPrefix (prefix );
  50.                 }
  51.                
  52.                 // Dummy implementation - not used!
  53.                 public Iterator getPrefixes ( String val ) {
  54.                     return null;
  55.                 }
  56.                
  57.                 // Dummy implemenation - not used!
  58.                 public String getPrefix ( String uri ) {
  59.                     return null;
  60.                 }
  61.             };
  62.            
  63.             // Now the XPath expression
  64.            
  65.             String xpathStr =
  66.                 "//ord:Order/ord:Description";
  67.             XPathFactory xpathFact =
  68.                 XPathFactory. newInstance ( );
  69.             XPath xpath = xpathFact. newXPath ( );
  70.             xpath. setNamespaceContext (ctx );
  71.             String result = xpath. evaluate (xpathStr, doc );
  72.             System. out. println ( "XPath result is /"" +
  73.                     result + "/"" );
  74.         }
  75.         catch ( Exception ex ) {
  76.             ex. printStackTrace ( );
  77.         }
  78.     }
  79. }

 

Try it out! It produces the output you expected, right? Well, only if you expected Java to throw up a stack trace ;-)

It will say something like javax.xml.transform.TransformerException: Prefix must resolve to a namespace: ord

But we did map ord to a namespace URI in the XML document, so why does that Xalan tool not see it? Is there a bug? Well, you might argue a design flaw, since it only uses the mapping found at the node passed to it, i.e., to top level namespace declarations in our case. So, let us move the namespace declaration to the top level, to get the following XML input

XML:
  1. <?xml version="1.0" ?>
  2. <Sales xmlns="http://www.davber.com/sales-format"
  3.    xmlns: ord= "http://www.davber.com/order-format" >
  4.   <Customer name="CostCo, Inc.">
  5.     <ord:Order price="12000">
  6.         <ord:Description>A bunch of stuff </ord:Description>
  7.     </ord:Order>
  8.   </Customer>
  9. </Sales>

 

Now you will see the output XPath result is "A bunch of stuff"

So what about the default namespace? Do we need a prefix there, and in that case which one?

We try with

XPATH:
/Sales/Customer/@name

 

Hmm, no match... So, it is not that simple. Again, we have to use a namespace in the XPath since the elements we try to match against are in a namespace (though it happens to be the default namespace) and the only way to use a namespace is via a prefix. What is that illusive prefix we need to map, via the Xalan prefix resolver, to the default namespace? The answer is: the empty string, i.e., the following XPath will match:

XPATH:
/:Sales/:Customer/@name

 

Yes, the output is XPath result is "CostCo, Inc."

So, the "Xalan" path allows us to quite easily deal with both explicitly prefixed elements and defaulted elements in the XML input. Note: this route is pretty fragile, since we here rely on a quite arbitrary choice of prefixes for those namespace URIs. I.e., if the XML author changes the prefix we are doomed. So, my recommendation is to use the URI via explicit mapping if you want/need to deal with namespace at all ;-)

How can we not care about the namespaces in the XML input? The only way is to disable namespace awareness, via the method invocation

JAVA:
  1. xmlFact. setNamespaceAware ( false );

 

Whether the factory is namespace aware or not by default is implementation-specific. Well, it should not be, since the SAX 2 Specification states that it should be namespace aware by default. It just happens that the Xerces parser of the J2SE reference implementation is not aware by default, so we could just leave the method invocation out of the picture. Let us set it explicitly to be on the safe side, and to support other implementations of JAXP... Thus, we get the following code:

JAVA:
  1. package com. davber. test;
  2.  
  3. import java.util.Iterator;
  4.  
  5. import javax.xml.namespace.NamespaceContext;
  6. import javax.xml.parsers.*;
  7. import javax.xml.xpath.*;
  8.  
  9. import org.w3c.dom.Document;
  10.  
  11. import com.sun.org.apache.xml.internal.utils.PrefixResolver;
  12. import com.sun.org.apache.xml.internal.utils.PrefixResolverDefault;
  13.  
  14. public class App
  15. {
  16.     public static void main ( String [ ] args )
  17.     {
  18.         try {
  19.             // First, the XML document
  20.            
  21.             String xmlStr =
  22.                 "<?xml version=/"1.0/" ?>/n" +
  23.                 "<Sales xmlns=/"http://www.davber.com/sales-format/" " +
  24.                 "xmlns:ord=/"http://www.davber.com/order-format/">/n" +
  25.                 "<Customer name=/"CostCo, Inc./">/n" +
  26.                 "<ord:Order " +
  27.                 "price=/"12000/">/n" +
  28.                 "<ord:Description>A bunch of stuff" +
  29.                 "</ord:Description>/n" +
  30.                 "</ord:Order>/n" +
  31.                 "</Customer>/n" +
  32.                 "</Sales>/n";
  33.             DocumentBuilderFactory xmlFact =
  34.                 DocumentBuilderFactory. newInstance ( );
  35.             xmlFact. setNamespaceAware ( false );
  36.             DocumentBuilder builder = xmlFact.
  37.                 newDocumentBuilder ( );
  38.             Document doc = builder. parse (
  39.                     new java. io. ByteArrayInputStream (
  40.                             xmlStr. getBytes ( ) ) );
  41.            
  42.             // We map the prefixes to URIs, using Xalan's
  43.             // document-extracting mapping tool and wrapping
  44.             // it in a nice JAXP shell
  45.            
  46.             final PrefixResolver resolver =
  47.                 new PrefixResolverDefault (doc. getDocumentElement ( ) );
  48.             NamespaceContext ctx = new NamespaceContext ( ) {
  49.                 public String getNamespaceURI ( String prefix ) {
  50.                     return resolver. getNamespaceForPrefix (prefix );
  51.                 }
  52.                
  53.                 // Dummy implementation - not used!
  54.                 public Iterator getPrefixes ( String val ) {
  55.                     return null;
  56.                 }
  57.                
  58.                 // Dummy implemenation - not used!
  59.                 public String getPrefix ( String uri ) {
  60.                     return null;
  61.                 }
  62.             };
  63.            
  64.             // Now the XPath expression
  65.            
  66.             String xpathStr =
  67.                 "/:Sales/:Customer/@name";
  68.             XPathFactory xpathFact =
  69.                 XPathFactory. newInstance ( );
  70.             XPath xpath = xpathFact. newXPath ( );
  71.             xpath. setNamespaceContext (ctx );
  72.             String result = xpath. evaluate (xpathStr, doc );
  73.             System. out. println ( "XPath result is /"" +
  74.                     result + "/"" );
  75.         }
  76.         catch ( Exception ex ) {
  77.             ex. printStackTrace ( );
  78.         }
  79.     }
  80. }

 

It is interesting to note that with namespace awareness disabled, namespaced XPath names do not match, i.e., they live in an alternative universe. The XPath evaluation is still namespace sensitive, but the XML document now resides in the non-namespaced corner of that world. The only way to get back to the namespace-agnostic universe is to leave the prefixes out altogether, such as in

XPATH:
/Sales/Customer/@name

 

Alas, we are back to square one!

There are two important issues we have to consider:

  1. try to apply a namespaced XPath to a document not declaring those namespaces will throw an exception
  2. other implementations of JAXP (earlier versions or not) might act a bit differently

The second issue is ignored for now, but please make sure you have the classpath setup correctly, so that the right implementation is at play; this classpath issue came to bite me a few days ago, effectively wasting a full day of work! The first issue is best explained by removing the prefix ord along with URI in the XML input, while keeping it in the XPath. I.e., we will have the following XML input and XPath expressions.

XML:
  1. <?xml version="1.0" ?>
  2. <Sales xmlns="http://www.davber.com/sales-format">
  3.   <Customer name="CostCo, Inc.">
  4.     <Order price="12000">
  5.         <Description>A bunch of stuff </Description>
  6.     </Order>
  7.   </Customer>
  8. </Sales>

 

XPATH:
//ord:Order/ord:Description

We here assume the same namespace mapping as before, either via an extraction - using the Xalan tool above - against the original document or with the explicit NamespaceContext we used initially. Using the latter method, we get the following code:

JAVA:
  1. package com. davber. test;
  2.  
  3. import java.util.Iterator;
  4.  
  5. import javax.xml.namespace.NamespaceContext;
  6. import javax.xml.parsers.*;
  7. import javax.xml.xpath.*;
  8.  
  9. import org.w3c.dom.Document;
  10.  
  11. public class App
  12. {
  13.     public static void main ( String [ ] args )
  14.     {
  15.         try {
  16.             // First, the XML document
  17.            
  18.             String xmlStr =
  19.                 "<?xml version=/"1.0/" ?>/n" +
  20.                 "<Sales xmlns=/"http://www.davber.com/sales-format/">/n " +
  21.                 "<Customer name=/"CostCo, Inc./">/n" +
  22.                 "<ord:Order " +
  23.                 "price=/"12000/">/n" +
  24.                 "<ord:Description>A bunch of stuff" +
  25.                 "</ord:Description>/n" +
  26.                 "</ord:Order>/n" +
  27.                 "</Customer>/n" +
  28.                 "</Sales>/n";
  29.             DocumentBuilderFactory xmlFact =
  30.                 DocumentBuilderFactory. newInstance ( );
  31.             xmlFact. setNamespaceAware ( true );
  32.             DocumentBuilder builder = xmlFact.
  33.                 newDocumentBuilder ( );
  34.             Document doc = builder. parse (
  35.                     new java. io. ByteArrayInputStream (
  36.                             xmlStr. getBytes ( ) ) );
  37.            
  38.             NamespaceContext ctx = new NamespaceContext ( ) {
  39.                 public String getNamespaceURI ( String prefix ) {
  40.                     String uri;
  41.                     if (prefix. equals ( "ord" ) )
  42.                         uri = "http://www.davber.com/order-format";
  43.                     else if (prefix. equals ( "sal" ) )
  44.                         uri = "http://www.davber.com/sales-format";
  45.                     else
  46.                         uri = null;
  47.                     return uri;
  48.                 }
  49.                
  50.                 // Dummy implementation - not used!
  51.                 public Iterator getPrefixes ( String val ) {
  52.                     return null;
  53.                 }
  54.                
  55.                 // Dummy implemenation - not used!
  56.                 public String getPrefix ( String uri ) {
  57.                     return null;
  58.                 }
  59.             };
  60.            
  61.             // Now the XPath expression
  62.            
  63.             String xpathStr =
  64.                 "//ord:Order/ord:Description";
  65.             XPathFactory xpathFact =
  66.                 XPathFactory. newInstance ( );
  67.             XPath xpath = xpathFact. newXPath ( );
  68.             xpath. setNamespaceContext (ctx );
  69.             String result = xpath. evaluate (xpathStr, doc );
  70.             System. out. println ( "XPath result is /"" +
  71.                     result + "/"" );
  72.         }
  73.         catch ( Exception ex ) {
  74.             ex. printStackTrace ( );
  75.         }
  76.     }
  77. }

 

Try it out. It will generate a stack trace: [Fatal Error] :4:26: The prefix "ord" for element "ord:Order" is not bound. org.xml.sax.SAXParseException: The prefix "ord" for element "ord:Order" is not bound. at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(Unknown Source) at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(Unknown Source) at javax.xml.parsers.DocumentBuilder.parse(Unknown Source) at com.davber.test.App.main(App.java:34)

What was that other issue again? Ah, something about other implementations behaving differently than the reference implementation of J2SE! Let us try this conjecture by putting the J2EE 1.4 reference implementation in the classpath. We will use WSDP 1.6.

It is time to sum up our experiences with namespaces and XPath in Java.

Lessons Learned

For those of you who skipped the lengthy post and jumped right here: Welcome back!.

The lessons learned are - whereof most are applicable to XPath with namespaced XML input in any language:

  1. whenever an XML element belongs in a namespace, the XPath pattern must use the same namespace for names to match
  2. just as with XML documents, namespaces are given via prefixes in XPath
  3. the XPath evaluator does not care about prefixes at all, but only with the namespace URI
  4. the above lesson implies we need to map prefixes to URIs, which is done via the JAXP interface NamespaceContext
  5. even the default namespace is a namespace, and thus matching names have to be prefixed in XPath
  6. there is a Xalan-specific tool to extract prefix to URI mappings from a DOM node: PrefixResolverDefault
  7. using that Xalan resolver, you have to embed it in a proper JAXP NamespaceContext
  8. that Xalan resolver will map the empty prefix to the default namespace URI, so you have to use the syntax :elem in XPath order to match elem in the default namespace.
  9. using a namespace URI in the XPath that is not declared in the parsed XML document will throw an exception
  10. you can disable namespace awareness with the method setNamespaceAware of the document builder factory in JAXP.
  11. some implementations, such as Xerces, have namespace awareness disabled by default

     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的XPath是一种用于在XML文档中定位和选择元素的语言。它可以帮助你对XML进行解析和处理。在Java中,你可以使用javax.xml.xpath包来实现XPath的功能。 下面是一个简单的示例代码,演示了如何在Java中使用XPath来解析XML文档: ```java import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; import org.w3c.dom.NodeList; public class XPathExample { public static void main(String[] args) { try { // 创建一个 DocumentBuilder 对象,用于解析 XML 文档 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); // 解析 XML 文档,得到一个 Document 对象 Document document = builder.parse("path/to/your/xml/file.xml"); // 创建 XPath 对象 XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); // 编写 XPath 表达式 String expression = "/root/element/subelement"; // 编译 XPath 表达式 XPathExpression expr = xpath.compile(expression); // 在文档中执行 XPath 表达式,获取结果 NodeList nodeList = (NodeList) expr.evaluate(document, XPathConstants.NODESET); // 处理结果 for (int i = 0; i < nodeList.getLength(); i++) { System.out.println(nodeList.item(i).getTextContent()); } } catch (Exception e) { e.printStackTrace(); } } } ``` 在这个示例中,我们使用了javax.xml.xpath包中的类来实现XPath的功能。首先,我们创建一个 DocumentBuilder 对象来解析XML文档。然后,我们创建一个 XPath 对象,并编写一个XPath表达式来选择我们感兴趣的元素。接下来,我们将XPath表达式编译成 XPathExpression 对象,并在XML文档上执行该表达式,得到一个包含所选元素的NodeList。最后,我们遍历NodeList,并处理每个元素的内容。 希望这个示例能帮助你理解如何在Java中使用XPath进行XML解析和处理。如果有任何问题,请随时提问!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值