XPath in Java: Querying XML Documents with javax.xml.xpath
XPath is a query language for selecting nodes from an XML document. Java's javax.xml.xpath package provides XPath 1.0 evaluation against a parsed DOM document. XPath lets you extract a single value, a list of nodes, or evaluate boolean and numeric expressions: all with a concise path syntax rather than manual DOM traversal.
Setting Up: XPathFactory and XPath
Java
import javax.xml.xpath.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;
// 1. Parse the XML into a DOM Document
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder()
.parse(new ByteArrayInputStream(xmlBytes));
// 2. Create an XPath instance
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
// 3. Evaluate an expression — returns a String result
String title = (String) xpath.evaluate(
"/library/book[1]/title",
doc,
XPathConstants.STRING);
System.out.println(title); // e.g. "Clean Code"
XPath instances are not thread-safe. Create one per thread or use XPathExpression (compile once, evaluate concurrently is also not safe without synchronisation). Cache XPathFactory which is thread-safe.
XPath Return Types: XPathConstants
| XPathConstants | Java type returned | Use for |
|---|---|---|
STRING | String | Text content of first matching node |
NUMBER | Double | Numeric expressions, count() |
BOOLEAN | Boolean | Existence checks, predicates |
NODE | Node | First matching node |
NODESET | NodeList | Multiple matching nodes |
Java
XPath xpath = XPathFactory.newInstance().newXPath();
// Get a single string
String author = (String) xpath.evaluate("/library/book[2]/author", doc, XPathConstants.STRING);
// Get a NodeList of all books
NodeList books = (NodeList) xpath.evaluate("//book", doc, XPathConstants.NODESET);
System.out.println("Books: " + books.getLength());
// Get a single Node
Node firstBook = (Node) xpath.evaluate("//book[1]", doc, XPathConstants.NODE);
// Count books
double count = (Double) xpath.evaluate("count(//book)", doc, XPathConstants.NUMBER);
System.out.println("Count: " + (int) count);
// Boolean check — does any book with year > 2010 exist?
boolean hasRecent = (Boolean) xpath.evaluate(
"count(//book[year > 2010]) > 0", doc, XPathConstants.BOOLEAN);
Common XPath Expressions Reference
| Expression | Selects |
|---|---|
/library | Root element named library |
//book | All book elements anywhere in the document |
/library/book[1] | First book child of library (1-indexed) |
/library/book[last()] | Last book element |
//book[@id='2'] | book with attribute id="2" |
//book[@genre] | All books that have a genre attribute |
//book[year > 2010] | Books where year element value > 2010 |
//book/title/text() | Text nodes inside title elements |
//@id | All id attributes anywhere |
//book[contains(title,'Java')] | Books whose title contains "Java" |
count(//book) | Number of book elements |
string-length(//book[1]/title) | Length of first book's title |
normalize-space(//title) | Trimmed, collapsed whitespace of title |
Compiled XPathExpression: Reuse for Performance
If you evaluate the same XPath against many documents, compile it once with xpath.compile().
Java
XPath xpath = XPathFactory.newInstance().newXPath();
// Compile once — reuse many times
XPathExpression titleExpr = xpath.compile("//book/title");
XPathExpression authorExpr = xpath.compile("//book/author");
// Evaluate against different documents
for (Document doc : documents) {
NodeList titles = (NodeList) titleExpr.evaluate(doc, XPathConstants.NODESET);
NodeList authors = (NodeList) authorExpr.evaluate(doc, XPathConstants.NODESET);
// process...
}
XPathExpression is also not thread-safe. Do not share compiled expressions across threads without synchronisation. Use a ThreadLocal<XPathExpression> or create a new one per evaluation in concurrent code.
Namespace-Aware XPath with NamespaceContext
To query namespace-qualified elements, register a NamespaceContext on the XPath instance.
Java
import javax.xml.namespace.NamespaceContext;
import java.util.Iterator;
// XML: <bk:library xmlns:bk="http://example.com/books"><bk:book>...
XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(new NamespaceContext() {
@Override
public String getNamespaceURI(String prefix) {
return switch (prefix) {
case "bk" -> "http://example.com/books";
default -> javax.xml.XMLConstants.NULL_NS_URI;
};
}
@Override public String getPrefix(String uri) { return null; }
@Override public Iterator<String> getPrefixes(String uri) { return null; }
});
// Now use the registered prefix in XPath expressions
NodeList books = (NodeList) xpath.evaluate(
"//bk:book", doc, XPathConstants.NODESET);
System.out.println("Found: " + books.getLength());
Evaluating Against an InputSource Directly
XPath can evaluate directly against an InputSource without building a DOM first: useful for one-off queries on large files.
Java
XPath xpath = XPathFactory.newInstance().newXPath();
// Evaluate against a file stream — no full DOM built
String title = (String) xpath.evaluate(
"/library/book[1]/title",
new org.xml.sax.InputSource(new FileInputStream("library.xml")),
XPathConstants.STRING);
System.out.println(title);
When evaluating against an
InputSource, the XPath engine builds an internal minimal representation. For multiple expressions on the same document, parse to DOM first and reuse the Document, evaluating repeatedly from InputSource re-parses each time.