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.

Share

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

XPathConstantsJava type returnedUse for
STRINGStringText content of first matching node
NUMBERDoubleNumeric expressions, count()
BOOLEANBooleanExistence checks, predicates
NODENodeFirst matching node
NODESETNodeListMultiple 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

ExpressionSelects
/libraryRoot element named library
//bookAll 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
//@idAll 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.