XPath: Check if a Node or Attribute Exists in Java

Check if a node or attribute exists with XPath in Java using boolean(), NODESET, NODE or count(), and tell missing elements from empty ones.

How XPath converts a path result for existence checks: boolean() gives true or false, count() gives a number, and string() gives an empty string for both missing and empty nodes

The XPath function boolean() is the shortest way to check if a node or attribute exists in Java, because it returns true when the path selects at least one node and false when it selects none. The same rule works for elements, attributes and text nodes.

We check for nodes before reading optional parts of XML, such as an optional element in a partner feed, a fault element in a SOAP response, or a setting that older configuration files do not have.

The following example checks four paths in an employees.xml file with Java 25.

Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("employees.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();
boolean hasEmployee = xpath.evaluateExpression("boolean(//employee)", doc, Boolean.class);               // true
boolean hasIdAttribute = xpath.evaluateExpression("boolean(//employee/@id)", doc, Boolean.class);        // true
boolean hasManager = xpath.evaluateExpression("boolean(//employee/manager)", doc, Boolean.class);        // false
boolean brianHasStatus = xpath.evaluateExpression("boolean(//employee[@id='2']/@status)", doc, Boolean.class);   // false

Notice that a missing node is not an error in XPath. It is an empty node-set that converts to false. We compare the four ways to run the check, look at attributes, separate missing nodes from empty ones, and build a small helper. The Java XPath tutorial explains the API itself.

All examples use the following file. The first employee has a status attribute and an empty middleName element, and the second has neither.

<?xml version="1.0" encoding="UTF-8"?>
<employees>
  <employee id="1" status="active">
    <firstName>Lokesh</firstName>
    <lastName>Gupta</lastName>
    <middleName/>
    <department>
      <id>101</id>
      <name>IT</name>
    </department>
  </employee>
  <employee id="2">
    <firstName>Brian</firstName>
    <lastName>Schultz</lastName>
    <department>
      <id>102</id>
      <name>HR</name>
    </department>
  </employee>
</employees>

1. Four Ways to Check if a Node or Attribute Exists

XPath 1.0 converts a node-set to a boolean by one rule, an empty set is false and any other set is true. We can let XPath do that conversion, or fetch the nodes and test them in Java.

How XPath converts a path result for existence checks: boolean() gives true or false, count() gives a number, and string() gives an empty string for both missing and empty nodes
boolean() is the reliable existence test; string() cannot tell a missing node from an empty one
ApproachExpression and result typeReturns when missingUse it when
boolean()boolean(//employee/manager), Boolean.classfalseWe only need yes or no
Node-set length//employee/manager, XPathConstants.NODESETgetLength() is 0We need the nodes afterwards
Single node//employee/manager, XPathConstants.NODEnullWe need the first match only
count()count(//employee/manager), Integer.class0We need the number of matches

We run all four approaches against the same file. The node-set and the single-node checks match the techniques many older answers use, and they return the matching nodes as well.

Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("employees.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();

Boolean viaBoolean = (Boolean) xpath.evaluate("//employee/@id", doc, XPathConstants.BOOLEAN);   // true
NodeList ids = (NodeList) xpath.evaluate("//employee/@id", doc, XPathConstants.NODESET);
boolean viaNodeSet = ids.getLength() > 0;                                                       // true
Node manager = (Node) xpath.evaluate("//employee/manager", doc, XPathConstants.NODE);
boolean viaNode = manager != null;                                                              // false
Integer idCount = xpath.evaluateExpression("count(//employee/@id)", doc, Integer.class);        // 2

Passing XPathConstants.BOOLEAN without boolean() in the expression gives the same result, because Java asks XPath to convert the node-set. Wrapping the path in boolean() makes the intent visible to the next reader.

2. Checking if an Attribute Exists

Attributes are nodes too, so the same checks work with an @ step. The predicate form [@name] selects elements that have the attribute, and [not(@name)] selects those without it, which is often the more useful question.

Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("employees.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();

boolean anyStatus = xpath.evaluateExpression("boolean(//employee/@status)", doc, Boolean.class);           // true
boolean firstHasStatus = xpath.evaluateExpression("boolean(//employee[1]/@status)", doc, Boolean.class);   // true
boolean activeExists = xpath.evaluateExpression("boolean(//employee[@status='active'])", doc, Boolean.class);   // true
boolean id3Exists = xpath.evaluateExpression("boolean(//employee[@id='3'])", doc, Boolean.class);          // false
String withoutStatus = xpath.evaluate("//employee[not(@status)]/firstName", doc);                         // "Brian"

Say an HR system imports employee files from several branch offices, and only newer offices send the status attribute. The importer checks //employee[not(@status)] first and fills a default value for those employees, instead of failing on a missing attribute later.

3. Missing Nodes vs Empty Nodes

Never test existence by reading the value as a string and comparing it with an empty string. The XPath string() conversion returns an empty string both for a missing node and for an element without text, so the two cases look the same.

Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("employees.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();

String emptyValue = xpath.evaluate("//employee[1]/middleName", doc);                                       // ""
String missingValue = xpath.evaluate("//employee[2]/middleName", doc);                                     // ""
boolean emptyExists = xpath.evaluateExpression("boolean(//employee[1]/middleName)", doc, Boolean.class);    // true
boolean missingExists = xpath.evaluateExpression("boolean(//employee[2]/middleName)", doc, Boolean.class);  // false
boolean hasText = xpath.evaluateExpression("boolean(//employee[1]/middleName[normalize-space()])", doc, Boolean.class);   // false

The predicate [normalize-space()] keeps only elements with non-blank text, so it answers a third question, namely whether the node exists and has a value. Pick the check that matches the rule in the business requirement.

QuestionExpressionemployee 1employee 2
Does the element exist?boolean(middleName)truefalse
Does it have text?boolean(middleName[normalize-space()])falsefalse
What is its text?string(middleName)“”“”

4. A Reusable exists() Helper

XPath 1.0, which the JDK implements, has no exists() function, so a small Java method fills the gap. It takes a context object, which is either the whole Document or one element, so relative paths work inside a loop.

static boolean exists(XPath xpath, Object context, String path) throws XPathExpressionException {
    return xpath.evaluateExpression("boolean(" + path + ")", context, Boolean.class);
}
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("employees.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();
List<String> withMiddleName = new ArrayList<>();
for (Node employee : xpath.evaluateExpression("//employee", doc, XPathNodes.class)) {
    if (exists(xpath, employee, "middleName")) {
        withMiddleName.add(xpath.evaluate("firstName", employee));
    }
}
List<String> result = withMiddleName;                                     // [Lokesh]
boolean deptName = exists(xpath, doc, "//employee[2]/department/name");   // true

The helper builds the expression from a path that we write in code. When part of the path comes from user input, such as an ID from a request, we pass it as an XPath variable instead of concatenating it, as shown in the XPath tutorial.

4.1. Real-World Example With a SOAP Fault

A payment gateway returns either a result element or a Fault element in its SOAP response, both in the SOAP namespace. The client checks for the fault first, and local-name() lets a quick check ignore the namespace prefix that the server picked.

String response = """
        <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
          <soap:Body>
            <soap:Fault><faultstring>Card expired</faultstring></soap:Fault>
          </soap:Body>
        </soap:Envelope>""";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
Document soap = factory.newDocumentBuilder().parse(new InputSource(new StringReader(response)));
XPath xpath = XPathFactory.newInstance().newXPath();

boolean plainPath = exists(xpath, soap, "//Fault");                                // false
boolean isFault = exists(xpath, soap, "//*[local-name()='Fault']");                // true
String reason = xpath.evaluate("//*[local-name()='faultstring']", soap);           // "Card expired"

The plain //Fault path returns false because the element belongs to the SOAP namespace. For code that reads a known format every day, a NamespaceContext with fixed prefixes is stricter, as described in the XPath namespace resolution article.

5. Checking Without XPath

For a simple element name or an attribute on an element we already hold, the DOM API answers without an expression. The Element.hasAttribute() method checks one attribute, and getElementsByTagName() searches all descendants by name.

Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("employees.xml"));
Element first = (Element) doc.getElementsByTagName("employee").item(0);
Element second = (Element) doc.getElementsByTagName("employee").item(1);

boolean firstStatus = first.hasAttribute("status");                          // true
boolean secondStatus = second.hasAttribute("status");                        // false
boolean anyManager = doc.getElementsByTagName("manager").getLength() > 0;    // false

The DOM methods are fine for one level and one name. As soon as the check includes a parent, a position or a value, such as an employee with ID 2 that has a department name, one XPath expression stays shorter than nested loops. The DOM parser article covers the DOM API.

6. XPath Node Existence FAQs

Most existence questions come from code that copied an XPath 2.0 function or compared strings.

6.1. Does Java XPath support the exists() function?

No. The exists() function belongs to XPath 2.0, and the JDK implements XPath 1.0, so the expression fails when it is compiled. Use boolean(path) or the helper from section 4.

Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("employees.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();
Boolean result = xpath.evaluateExpression("exists(//employee)", doc, Boolean.class);   // XPathExpressionException

6.2. Why does XPath return an empty string for a node that does not exist?

Because evaluate() without a result type converts the result to a string, and XPath converts an empty node-set to an empty string. As shown in section 3, an empty element gives the same result, so use boolean() for existence.

6.3. How do I check that several nodes exist at once?

Combine the paths with and inside one expression, for example boolean(//employee/firstName) and boolean(//employee/lastName). To check that every employee has a last name, test for the opposite case, not(//employee[not(lastName)]).

6.4. Is count() slower than boolean()?

Sometimes, on large documents. The count() function must visit every match, whereas a boolean conversion may stop at the first one, depending on the XPath engine. The difference matters only for big documents, and boolean() also reads better.

7. Conclusion

The XPath function boolean() is the simplest way to check if a node or attribute exists in Java, and XPathConstants.NODESET, XPathConstants.NODE and count() give the same answer when we also need the nodes or their number.

Missing and empty elements both read as an empty string, so existence checks never compare text. The JDK has no exists() function, a small helper with a context node covers that gap, and local-name() or a NamespaceContext keeps the check working on namespaced XML.

8. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. This is useless, because most xml is namespace defined. This is useless unless you provide xpath a way to determine the namespace

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.