CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-xmlunit--xmlunit-core

XMLUnit Core is a comprehensive XML testing library for Java that provides powerful tools for comparing XML documents, validating XML against schemas, and evaluating XPath expressions.

Overview
Eval results
Files

xpath.mddocs/

XPath Evaluation

XPath 1.0 expression evaluation with namespace support, custom XPath functions, and various return type handling for node sets, strings, numbers, and booleans. XMLUnit provides a unified XPath engine interface with JAXP-based implementation.

Capabilities

XPath Engine Interface

The core interface for evaluating XPath expressions against XML documents.

public interface XPathEngine {
    /** Evaluate XPath expression and return string result */
    String evaluate(String xPath, Source s);
    
    /** Evaluate XPath expression and return string result */
    String evaluate(String xPath, Node n);
    
    /** Select nodes matching XPath expression from Source */
    Iterable<Node> selectNodes(String xPath, Source s);
    
    /** Select nodes matching XPath expression from Node */
    Iterable<Node> selectNodes(String xPath, Node n);
    
    /** Configure namespace prefixes for XPath evaluation */
    void setNamespaceContext(Map<String, String> prefix2Uri);
}

JAXP XPath Engine

The standard JAXP-based implementation of the XPath engine.

/**
 * JAXP-based XPath engine implementation
 * Uses javax.xml.xpath API for XPath 1.0 evaluation
 */
public class JAXPXPathEngine implements XPathEngine {
    /** Create new XPath engine with default configuration */
    public JAXPXPathEngine();
    
    /** Configure XPath function resolver for custom functions */
    public void setXPathFunctionResolver(XPathFunctionResolver resolver);
    
    /** Configure XPath variable resolver for XPath variables */
    public void setXPathVariableResolver(XPathVariableResolver resolver);
    
    /** Configure namespace context for namespace-aware XPath */
    @Override
    public void setNamespaceContext(NamespaceContext ctx);
    
    /** Configure namespace context using simple String mapping */
    public void setNamespaceContext(Map<String, String> prefix2Uri);
}

Usage Examples:

import org.xmlunit.xpath.JAXPXPathEngine;
import org.xmlunit.builder.Input;
import javax.xml.transform.Source;
import org.w3c.dom.Node;

// Basic XPath evaluation
JAXPXPathEngine xpath = new JAXPXPathEngine();
Source xmlSource = Input.fromString("""
    <users>
        <user id="1">
            <name>John Doe</name>
            <email>john@example.com</email>
            <age>30</age>
        </user>
        <user id="2">
            <name>Jane Smith</name>
            <email>jane@example.com</email>
            <age>25</age>
        </user>
    </users>
    """).build();

// Evaluate XPath to get string result
String firstUserName = xpath.evaluate("/users/user[1]/name/text()", xmlSource);
System.out.println("First user: " + firstUserName); // "John Doe"

String userCount = xpath.evaluate("count(/users/user)", xmlSource);
System.out.println("User count: " + userCount); // "2"

// Select nodes matching XPath
Iterable<Node> userNodes = xpath.selectNodes("//user[@id]", xmlSource);
for (Node user : userNodes) {
    String id = xpath.evaluate("@id", user);
    String name = xpath.evaluate("name/text()", user);
    System.out.println("User " + id + ": " + name);
}

Namespace Support

Configure namespace prefixes for namespace-aware XPath evaluation.

// Using Map<String, String> for simple namespace mapping
public void setNamespaceContext(Map<String, String> prefix2Uri);

// Using full NamespaceContext for advanced scenarios
public void setNamespaceContext(NamespaceContext ctx);

Namespace Examples:

import org.xmlunit.xpath.JAXPXPathEngine;
import org.xmlunit.builder.Input;
import java.util.HashMap;
import java.util.Map;

// XML with namespaces
String namespacedXml = """
    <?xml version="1.0"?>
    <root xmlns:p="http://example.com/person" xmlns:a="http://example.com/address">
        <p:person id="1">
            <p:name>John Doe</p:name>
            <a:address>
                <a:street>123 Main St</a:street>
                <a:city>Anytown</a:city>
            </a:address>
        </p:person>
    </root>
    """;

JAXPXPathEngine xpath = new JAXPXPathEngine();

// Configure namespace prefixes
Map<String, String> namespaces = new HashMap<>();
namespaces.put("person", "http://example.com/person");
namespaces.put("addr", "http://example.com/address");
xpath.setNamespaceContext(namespaces);

Source xmlSource = Input.fromString(namespacedXml).build();

// Use prefixes in XPath expressions
String personName = xpath.evaluate("//person:name/text()", xmlSource);
String city = xpath.evaluate("//addr:city/text()", xmlSource);

System.out.println("Name: " + personName); // "John Doe"
System.out.println("City: " + city);       // "Anytown"

// Select nodes with namespaces
Iterable<Node> addressNodes = xpath.selectNodes("//addr:address", xmlSource);
for (Node address : addressNodes) {
    String street = xpath.evaluate("addr:street/text()", address);
    System.out.println("Street: " + street);
}

Custom Functions and Variables

Extend XPath evaluation with custom functions and variables.

/** Configure XPath function resolver for custom functions */
public void setXPathFunctionResolver(XPathFunctionResolver resolver);

/** Configure XPath variable resolver for XPath variables */
public void setXPathVariableResolver(XPathVariableResolver resolver);

Custom Function Examples:

import javax.xml.xpath.XPathFunction;
import javax.xml.xpath.XPathFunctionResolver;
import javax.xml.namespace.QName;

// Custom XPath function
public class UpperCaseFunction implements XPathFunction {
    @Override
    public Object evaluate(List args) throws XPathFunctionException {
        if (args.size() != 1) {
            throw new XPathFunctionException("upper-case() requires exactly one argument");
        }
        return args.get(0).toString().toUpperCase();
    }
}

// Function resolver
XPathFunctionResolver functionResolver = new XPathFunctionResolver() {
    @Override
    public XPathFunction resolveFunction(QName functionName, int arity) {
        if ("upper-case".equals(functionName.getLocalPart()) && arity == 1) {
            return new UpperCaseFunction();
        }
        return null;
    }
};

JAXPXPathEngine xpath = new JAXPXPathEngine();
xpath.setXPathFunctionResolver(functionResolver);

// Use custom function in XPath
String upperName = xpath.evaluate("upper-case(/users/user[1]/name)", xmlSource);
System.out.println("Upper case name: " + upperName); // "JOHN DOE"

Variable Examples:

import javax.xml.xpath.XPathVariableResolver;
import javax.xml.namespace.QName;

// Variable resolver
XPathVariableResolver variableResolver = new XPathVariableResolver() {
    @Override
    public Object resolveVariable(QName variableName) {
        if ("minAge".equals(variableName.getLocalPart())) {
            return 25;
        }
        return null;
    }
};

JAXPXPathEngine xpath = new JAXPXPathEngine();
xpath.setXPathVariableResolver(variableResolver);

// Use variables in XPath
Iterable<Node> adults = xpath.selectNodes("//user[age >= $minAge]", xmlSource);
for (Node user : adults) {
    String name = xpath.evaluate("name/text()", user);
    String age = xpath.evaluate("age/text()", user);
    System.out.println(name + " (age " + age + ")");
}

XPath Expression Types

Handle different XPath result types and evaluation patterns.

String Results:

// Text content
String text = xpath.evaluate("//user[1]/name/text()", xmlSource);

// Attribute values
String id = xpath.evaluate("//user[1]/@id", xmlSource);

// Concatenated results
String fullName = xpath.evaluate("concat(//user[1]/name, ' (', //user[1]/@id, ')')", xmlSource);

// Numeric results as strings
String count = xpath.evaluate("count(//user)", xmlSource);
String sum = xpath.evaluate("sum(//user/age)", xmlSource);

Node Selection:

// Select multiple nodes
Iterable<Node> allUsers = xpath.selectNodes("//user", xmlSource);

// Select with predicates
Iterable<Node> adultUsers = xpath.selectNodes("//user[age >= 21]", xmlSource);

// Select attributes
Iterable<Node> userIds = xpath.selectNodes("//user/@id", xmlSource);

// Complex selections
Iterable<Node> specificUsers = xpath.selectNodes("//user[contains(name, 'John') or age > 25]", xmlSource);

Boolean and Numeric XPath (evaluated as strings):

// Boolean expressions return "true" or "false"
String hasUsers = xpath.evaluate("count(//user) > 0", xmlSource);
String hasAdults = xpath.evaluate("//user[age >= 18]", xmlSource);

// Numeric expressions return string representations
String avgAge = xpath.evaluate("sum(//user/age) div count(//user)", xmlSource);
String maxAge = xpath.evaluate("//user[age = max(//user/age)]/age", xmlSource);

Common XPath Patterns

Frequently used XPath patterns and examples for XML processing.

Element Selection:

// Root element
Node root = xpath.selectNodes("/*", xmlSource).iterator().next();

// All elements of specific type
Iterable<Node> users = xpath.selectNodes("//user", xmlSource);

// Elements by position
Node firstUser = xpath.selectNodes("//user[1]", xmlSource).iterator().next();
Node lastUser = xpath.selectNodes("//user[last()]", xmlSource).iterator().next();

// Elements by attribute
Iterable<Node> adminUsers = xpath.selectNodes("//user[@role='admin']", xmlSource);

Attribute Access:

// All attributes of an element
Iterable<Node> userAttrs = xpath.selectNodes("//user[1]/@*", xmlSource);

// Specific attributes
String userId = xpath.evaluate("//user[1]/@id", xmlSource);
String userRole = xpath.evaluate("//user[@id='123']/@role", xmlSource);

Text Content:

// Element text content
String userName = xpath.evaluate("//user[1]/name/text()", xmlSource);

// All text content (including descendants)
String allText = xpath.evaluate("//user[1]//text()", xmlSource);

// Normalized text content
String normalizedText = xpath.evaluate("normalize-space(//user[1]/description)", xmlSource);

Conditional Selection:

// Existence checks
Iterable<Node> usersWithEmail = xpath.selectNodes("//user[email]", xmlSource);
Iterable<Node> usersWithoutEmail = xpath.selectNodes("//user[not(email)]", xmlSource);

// Value comparisons
Iterable<Node> youngUsers = xpath.selectNodes("//user[age < 25]", xmlSource);
Iterable<Node> seniorUsers = xpath.selectNodes("//user[age >= 65]", xmlSource);

// String matching
Iterable<Node> johnsAndJanes = xpath.selectNodes("//user[starts-with(name, 'J')]", xmlSource);
Iterable<Node> gmailUsers = xpath.selectNodes("//user[contains(email, 'gmail')]", xmlSource);

Error Handling

Handle XPath evaluation errors and edge cases.

try {
    JAXPXPathEngine xpath = new JAXPXPathEngine();
    
    // Evaluate XPath expression
    String result = xpath.evaluate("//user/name/text()", xmlSource);
    
    // Handle empty results
    if (result == null || result.isEmpty()) {
        System.out.println("No matching elements found");
    } else {
        System.out.println("Result: " + result);
    }
    
} catch (RuntimeException e) {
    // Handle XPath syntax errors
    if (e.getCause() instanceof XPathExpressionException) {
        System.err.println("Invalid XPath expression: " + e.getMessage());
    } else {
        System.err.println("XPath evaluation error: " + e.getMessage());
    }
}

// Check node iteration results
Iterable<Node> nodes = xpath.selectNodes("//nonexistent", xmlSource);
boolean hasResults = nodes.iterator().hasNext();
if (!hasResults) {
    System.out.println("XPath returned no nodes");
}

Integration with Other XMLUnit Components

XPath evaluation integrates with XMLUnit's input, comparison, and validation features.

// Use with different input sources
Source fileSource = Input.fromFile("data.xml").build();
Source jaxbSource = Input.fromJaxb(personObject).build();
Source transformedSource = Transform.source(originalXml)
    .usingStylesheet(stylesheetSource)
    .build();

// Evaluate XPath on all source types
String fileResult = xpath.evaluate("//name/text()", fileSource);
String jaxbResult = xpath.evaluate("//name/text()", jaxbSource);
String transformResult = xpath.evaluate("//name/text()", transformedSource);

// Use XPath in custom difference evaluators
DifferenceEvaluator xpathIgnore = (comparison, outcome) -> {
    // Ignore differences in elements matching specific XPath
    if (xpath.selectNodes("//timestamp", comparison.getControlDetails().getTarget()).iterator().hasNext()) {
        return ComparisonResult.SIMILAR;
    }
    return outcome;
};

// Use XPath with namespace context from validation
Map<String, String> validationNamespaces = new HashMap<>();
validationNamespaces.put("ns", "http://example.com/namespace");
xpath.setNamespaceContext(validationNamespaces);

String namespacedResult = xpath.evaluate("//ns:element/text()", xmlSource);

Install with Tessl CLI

npx tessl i tessl/maven-org-xmlunit--xmlunit-core

docs

comparison.md

index.md

input.md

transform.md

validation.md

xpath.md

tile.json