Java XPath Get Attribute Value (String, List, Number)

Java XPath get attribute value examples with @ in the expression. Read one value, all values as a List, numbers, missing attributes and secure parsing.

Tree of playlist.xml with element nodes and their attribute nodes, highlighting the path from playlist to the genre attribute of song s2

To get an attribute value with XPath in Java, we put @ in front of the attribute name in the expression, for example /playlist/song[@id=’s2′]/@genre, and call XPath.evaluate(expression, document), which returns the value as a String. The @ sign selects the attribute node, and the javax.xml.xpath API in the JDK evaluates it without any library.

We read attributes with XPath when the value we need sits deep in a document, such as a currency code on a price element, a version on a config entry or the id of every item in a feed. One expression replaces a loop over getElementsByTagName() calls and if checks.

The following example reads attributes from a small playlist file, playlist.xml.

<?xml version="1.0" encoding="UTF-8"?>
<playlist name="Road trip">
  <song id="s1" genre="rock" seconds="215">Highway Song</song>
  <song id="s2" genre="jazz" seconds="340">Blue Evening</song>
  <song id="s3" genre="rock" seconds="198" explicit="true">Engine Noise</song>
</playlist>
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
Document doc = factory.newDocumentBuilder().parse(new File("playlist.xml"));

XPath xpath = XPathFactory.newInstance().newXPath();
String genre = xpath.evaluate("/playlist/song[@id='s2']/@genre", doc);   // "jazz"
String name = xpath.evaluate("/playlist/@name", doc);                    // "Road trip"

Notice that the parser refuses DOCTYPE declarations, which keeps external entities out of untrusted files. The next sections explain the @ syntax and show how to read one value, all values, numbers instead of strings, and when Element.getAttribute() is the better tool.

1. Selecting Attributes With @ in an XPath Expression

In the XPath data model, an attribute is a node of its own that belongs to an element, but it is not a child of that element. A path such as /playlist/song walks the child axis and never reaches attributes. The @ abbreviation switches to the attribute axis, so song/@genre means “the genre attribute of each song element”.

Tree of playlist.xml with element nodes and their attribute nodes, highlighting the path from playlist to the genre attribute of song s2
The predicate picks the song element, and @genre steps from that element to its attribute node

A few expression shapes cover almost every case. The predicate in square brackets filters elements before @ picks the attribute, and @* matches every attribute of an element. The JDK parser does not keep the attribute order of the source file, so @* returns attributes sorted by name.

XPath expressionSelectsResult for playlist.xml
/playlist/@nameAttribute of the root elementRoad trip
//song/@genreThe attribute on every songrock, jazz, rock
//song[@id=’s2′]/@genreThe attribute on one filtered elementjazz
//song[2]/@secondsThe attribute on the second song340
//song[@id=’s3′]/@*All attributes of one elementtrue, rock, s3, 198 (sorted by attribute name)
@genreThe attribute of the context node (relative path)Depends on the node we pass in

The order of the steps matters. The expression //song/@genre returns attribute nodes, whereas //song[@genre] returns the song elements that have a genre attribute. The second form belongs to filtering, which the article on finding XML nodes by attribute value covers in depth.

2. Get a Single Attribute Value With XPath

The two-argument evaluate(String, Object) method returns a String. It applies the XPath string() function to the result, and that function has two rules we need to know. If the expression matches several nodes, it returns the value of the first one in document order. If it matches nothing, it returns an empty string, not null.

Every example from here on uses one small helper to load the file. It creates a fresh factory with DOCTYPE declarations turned off, which is the setting the OWASP XXE cheat sheet recommends for the JDK parser.

static Document loadXml(Path file) throws Exception {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
    return factory.newDocumentBuilder().parse(file.toFile());
}
Document doc = loadXml(Path.of("playlist.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();

String first = xpath.evaluate("//song/@genre", doc);                 // "rock"
String missing = xpath.evaluate("//song[@id='s9']/@genre", doc);     // ""
String noAttr = xpath.evaluate("//song[@id='s1']/@explicit", doc);   // ""
boolean exists = (Boolean) xpath.evaluate("boolean(//song[@id='s1']/@explicit)", doc, XPathConstants.BOOLEAN);   // false

The two empty strings look the same, although one song does not exist and the other song exists without the attribute. When the difference matters, we test with boolean(), which returns false for an empty node set. An attribute written as explicit=”” also returns an empty string, so boolean() is the only reliable presence check.

We never treat an empty string from evaluate() as proof that a value was set, because XPath reports a missing attribute and an empty attribute the same way. For example, a music import job that reads @seconds and passes it to Integer.parseInt() gets a NumberFormatException for a song without a duration, so it either checks presence first or reads the value as a number, as section 4 shows.

3. Getting All Values of an Attribute as a List

To read every match, we ask for a node set. Since Java 9, evaluateExpression() with XPathNodes returns an Iterable of nodes, so we can use a for-each loop. Each node is an Attr, and getNodeValue() returns its value.

Document doc = loadXml(Path.of("playlist.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();

XPathNodes nodes = xpath.evaluateExpression("//song/@id", doc, XPathNodes.class);
List<String> ids = new ArrayList<>();
for (Node node : nodes) {
    ids.add(node.getNodeValue());
}
List<String> allIds = ids;                 // [s1, s2, s3]
int count = nodes.size();                  // 3

Older code passes XPathConstants.NODESET and casts the result to NodeList. It works on every Java version, but NodeList is not Iterable, so the loop goes by index.

Document doc = loadXml(Path.of("playlist.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();

NodeList list = (NodeList) xpath.evaluate("//song/@genre", doc, XPathConstants.NODESET);
List<String> genres = new ArrayList<>();
for (int i = 0; i < list.getLength(); i++) {
    genres.add(list.item(i).getNodeValue());
}
List<String> allGenres = genres;           // [rock, jazz, rock]

Duplicates stay in the list, because XPath returns one attribute node per element. A LinkedHashSet removes them and keeps the document order.

4. Getting Numbers and Booleans Instead of Strings

Attribute values are always text in XML, but XPath can convert them. The evaluateExpression() method accepts Double, Integer, Long, Boolean and String as the target class, and XPath functions such as sum() and count() work on attribute values in the same expression.

Document doc = loadXml(Path.of("playlist.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();

Integer seconds = xpath.evaluateExpression("//song[@id='s2']/@seconds", doc, Integer.class);   // 340
Double total = xpath.evaluateExpression("sum(//song/@seconds)", doc, Double.class);            // 753.0
Double rockSongs = xpath.evaluateExpression("count(//song[@genre='rock'])", doc, Double.class); // 2.0
Double none = xpath.evaluateExpression("//song[@id='s9']/@seconds", doc, Double.class);        // NaN

A missing or non-numeric attribute becomes NaN as a Double, so we check Double.isNaN() before using it. The Integer conversion is less forgiving. It truncates NaN to 0, which hides a missing value, so Double is the safer target when the attribute is optional.

Document doc = loadXml(Path.of("playlist.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();

Integer hidden = xpath.evaluateExpression("//song[@id='s9']/@seconds", doc, Integer.class);    // 0

5. Reading Attributes of the Current Node

A common pattern selects elements with one expression and reads several attributes from each of them. The second argument of evaluate() is the context node, so a relative expression such as @genre runs against the element we pass in. For example, a music app that imports a partner playlist loops over the songs, reads three attributes from each and builds a record per song.

record Song(String id, String genre, int seconds) {}
Document doc = loadXml(Path.of("playlist.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();

List<Song> songs = new ArrayList<>();
for (Node node : xpath.evaluateExpression("//song", doc, XPathNodes.class)) {
    Element song = (Element) node;
    songs.add(new Song(
        xpath.evaluate("@id", song),
        song.getAttribute("genre"),
        Integer.parseInt(song.getAttribute("seconds"))));
}
Song second = songs.get(1);                // Song[id=s2, genre=jazz, seconds=340]

The loop mixes both styles on purpose. Once we hold an Element, getAttribute() is shorter and faster than a relative XPath, because no expression is parsed. XPath pays off for the first step, where it finds the elements anywhere in the tree.

NeedUseMissing attribute returns
Value from anywhere in the documentxpath.evaluate(“//a[@x=’1′]/@y”, doc)Empty string
Value on an element we already holdelement.getAttribute(“y”)Empty string
Presence check on an elementelement.hasAttribute(“y”)false
Presence check in a documentboolean(//a/@y) with XPathConstants.BOOLEANfalse
Numeric valueevaluateExpression(expr, doc, Double.class)NaN

6. Compiling an Expression Once and Reusing It

When the same expression runs many times, such as once per uploaded file in a web app, we compile it with compile() and keep the XPathExpression. Compiling parses the expression once, and each evaluate() call only walks the tree.

Document doc = loadXml(Path.of("playlist.xml"));
XPathExpression genreOf = XPathFactory.newInstance().newXPath().compile("@genre");

NodeList songs = doc.getElementsByTagName("song");
String firstGenre = genreOf.evaluate(songs.item(0));     // "rock"
String secondGenre = genreOf.evaluate(songs.item(1));    // "jazz"

The JDK does not make XPath or XPathExpression objects thread-safe. In a multi-threaded service, each thread needs its own instance, for example by compiling the expression inside the request method or by holding it in a ThreadLocal. Creating an XPath object is cheap compared with parsing the XML document.

7. XPath Attribute Value FAQs

Most attribute questions come up once the first expression works and real files start to arrive with namespaces, odd values or a different library.

7.1. What Is the Difference Between //song/@genre and //song[@genre]?

The first expression returns the genre attribute nodes, so evaluate() gives their values. The second returns the song elements that have a genre attribute, so evaluate() gives the text content of the first matching song, “Highway Song” in our file.

7.2. Why Does My XPath Return an Empty String for an Attribute That Exists?

In most cases, the document uses a namespace and the expression does not. An element such as <m:song xmlns:m=”…”> matches only a prefixed name test that is bound through a NamespaceContext. The article on XPath namespace resolution shows the setup. Plain attributes without a prefix are in no namespace, so @genre works once the element step matches.

7.3. How Do I Get an Attribute Value Without XPath?

We call getAttribute() on the Element, after finding it with getElementsByTagName() or by walking the tree. The DOM parser tutorial shows that approach, and JDOM2 offers getAttributeValue(), which returns null for a missing attribute, as the article on reading XML with JDOM2 explains.

7.4. Does the JDK Support XPath 2.0 or 3.1?

No. The javax.xml.xpath API in Java 25 implements XPath 1.0, so functions such as ends-with(), lower-case() and regular expressions are not available. Projects that need XPath 3.1 add a library such as Saxon-HE. For attribute values, XPath 1.0 covers everything shown here.

8. Conclusion

An attribute value in XPath is one @ away from its element. The expression //song[@id=’s2′]/@genre picks the element with a predicate and steps to its attribute, and evaluate() returns the value as a String, or an empty string when nothing matches.

For all values, we ask for XPathNodes and read getNodeValue() from each node. For numbers, evaluateExpression() with Double.class makes missing values visible as NaN. Inside a loop over elements, getAttribute() is simpler than another XPath call. More XPath syntax, such as axes and operators, is in the Java XPath tutorial.

9. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. I need to check child tag contain text() using xPath. I have one parent tag it have more child and Sub child. I need to check any of my child tag contain text(). If it may ,, or any customize tag too. and I nee to print it nodeName.

    <outcome>
    <item>
    <Block>
    <div class="h1">
    <p>Read directions.</p>
    <span>
    <obj>
    <div class="h2">
    <div class="h3"/>
    <div class="h4">
    <p class="h5">
    Read directions.
    </p>
    </div>
    </div>
    </obj>
    </span>
    </div>
    <graphic>
    <crome>
    <p>Jill table</p>
    </prompt>
    <obj>
    <p>NONE</p>
    </obj>
    <Img>
    <obj>
    <p>NONE</p>
    </obj>
    </Img>
    <Img>
    <obj>
    <p>NONE</p>
    </obj>
    </gapImg>
    <target>
    </graphic>
    </itemBody>
    </outcome>
    
    private static void addAttributeId(Node someNode, Document doc) {
    		  XPath xPath = XPathFactory.newInstance().newXPath();
    	       // NodeList nodesName;
    				try {
    					
    					XPathExpression expr =  xPath.compile("//itemBody/text()");
    					Object result = expr.evaluate(doc, XPathConstants.NODESET);
    					NodeList nodes = (NodeList) result;
    					for (int i = 0; i < nodes.getLength(); i++) {
    					    System.out.println(nodes.item(i).getNodeValue());
    					}
    					
    				}
    		catch (XPathExpressionException e) {
    			e.printStackTrace();
    			}
    	  	}	
    
  2. I am getting following exception at

    XPath xpathExpression = XPath.newInstance(“//*[@id]”);

    Exception in thread “main” java.lang.NoClassDefFoundError: org/jaxen/JaxenException at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:186) at org.jdom.xpath.XPath.newInstance(XPath.java:134) at xmlattributes.Main.main(Main.java:61)

    pl guide me me added jdom1.2.2 jar

    Rajendra

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.