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.
| 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 |
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);
| 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 |
If you evaluate the same XPath against many documents, compile it once with xpath.compile().
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.
To query namespace-qualified elements, register a NamespaceContext on the XPath instance.
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());
XPath can evaluate directly against an InputSource without building a DOM first — useful for one-off queries on large files.
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.