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.
Comprehensive XML comparison functionality for detecting structural differences, content variations, and attribute changes between XML documents. XMLUnit provides flexible comparison strategies with customizable difference evaluation and detailed reporting.
The main entry point for XML comparison operations, providing a fluent API for configuring and executing comparisons.
/**
* Creates a new DiffBuilder to compare XML documents
* @param control - The control (expected) XML document as any supported input type
* @returns DiffBuilder instance for configuration
*/
public static DiffBuilder compare(Object control);
public class DiffBuilder implements DifferenceEngineConfigurer<DiffBuilder> {
/** Set the test (actual) document to compare against control */
public DiffBuilder withTest(Object test);
/** Ignore whitespace-only text nodes */
public DiffBuilder ignoreWhitespace();
/** Normalize whitespace in text content */
public DiffBuilder normalizeWhitespace();
/** Ignore whitespace between elements */
public DiffBuilder ignoreElementContentWhitespace();
/** Ignore XML comments during comparison */
public DiffBuilder ignoreComments();
/** Ignore XML comments using specific XSLT version */
public DiffBuilder ignoreCommentsUsingXSLTVersion(String xsltVersion);
/** Check for similarity rather than identical match */
public DiffBuilder checkForSimilar();
/** Check for identical match (default) */
public DiffBuilder checkForIdentical();
/** Configure custom DocumentBuilderFactory */
public DiffBuilder withDocumentBuilderFactory(DocumentBuilderFactory f);
/** Build the final Diff instance */
public Diff build();
}Usage Examples:
import org.xmlunit.builder.Input;
import org.xmlunit.builder.DiffBuilder;
import org.xmlunit.diff.Diff;
// Basic XML comparison
Source control = Input.fromString("<users><user id='1'>John</user></users>").build();
Source test = Input.fromString("<users><user id='1'>Jane</user></users>").build();
Diff diff = DiffBuilder.compare(control)
.withTest(test)
.build();
if (diff.hasDifferences()) {
System.out.println("Documents differ: " + diff.toString());
}
// Ignore whitespace differences
Diff lenientDiff = DiffBuilder.compare(control)
.withTest(test)
.ignoreWhitespace()
.normalizeWhitespace()
.ignoreComments()
.build();
// Check for similarity instead of identical
Diff similarityDiff = DiffBuilder.compare(control)
.withTest(test)
.checkForSimilar()
.build();DiffBuilder extends DifferenceEngineConfigurer providing advanced customization options.
public interface DifferenceEngineConfigurer<D> {
/** Configure custom node matching strategy */
D withNodeMatcher(NodeMatcher nodeMatcher);
/** Configure custom difference evaluation */
D withDifferenceEvaluator(DifferenceEvaluator differenceEvaluator);
/** Configure comparison control flow */
D withComparisonController(ComparisonController comparisonController);
/** Add comparison event listeners */
D withComparisonListeners(ComparisonListener... listeners);
/** Add difference event listeners */
D withDifferenceListeners(ComparisonListener... listeners);
/** Configure namespace prefixes for XPath */
D withNamespaceContext(Map<String, String> prefix2Uri);
/** Filter attributes during comparison */
D withAttributeFilter(Predicate<Attr> attributeFilter);
/** Filter nodes during comparison */
D withNodeFilter(Predicate<Node> nodeFilter);
/** Configure comparison output formatting */
D withComparisonFormatter(ComparisonFormatter formatter);
}Usage Examples:
import org.xmlunit.diff.*;
// Custom difference evaluation
DifferenceEvaluator customEvaluator = new DifferenceEvaluator() {
@Override
public ComparisonResult evaluate(Comparison comparison, ComparisonResult outcome) {
// Treat attribute order differences as similar, not different
if (comparison.getType() == ComparisonType.ATTR_SEQUENCE) {
return ComparisonResult.SIMILAR;
}
return outcome;
}
};
Diff customDiff = DiffBuilder.compare(control)
.withTest(test)
.withDifferenceEvaluator(customEvaluator)
.build();
// Custom node matching
NodeMatcher elementMatcher = new DefaultNodeMatcher(
ElementSelectors.byNameAndText,
ElementSelectors.byName
);
Diff matchedDiff = DiffBuilder.compare(control)
.withTest(test)
.withNodeMatcher(elementMatcher)
.build();
// Namespace context for XPath evaluation
Map<String, String> namespaces = new HashMap<>();
namespaces.put("ns", "http://example.com/namespace");
Diff namespaceAwareDiff = DiffBuilder.compare(control)
.withTest(test)
.withNamespaceContext(namespaces)
.build();The result of a comparison operation containing all detected differences.
public class Diff {
/** Check if any differences were found */
public boolean hasDifferences();
/** Get all differences found during comparison */
public Iterable<Difference> getDifferences();
/** Get the control (expected) XML source */
public Source getControlSource();
/** Get the test (actual) XML source */
public Source getTestSource();
/** Get complete description of all differences */
public String fullDescription();
/** Get complete description using custom formatter */
public String fullDescription(ComparisonFormatter formatter);
/** String representation of first difference */
@Override
public String toString();
/** String representation using custom formatter */
public String toString(ComparisonFormatter formatter);
}Detailed information about specific differences found during comparison.
public class Difference {
/** Get the comparison that was performed */
public Comparison getComparison();
/** Get the result of the comparison */
public ComparisonResult getResult();
}
public class Comparison {
/** Get the type of comparison performed */
public ComparisonType getType();
/** Get details of the control (expected) side */
public Detail getControlDetails();
/** Get details of the test (actual) side */
public Detail getTestDetails();
/** Formatted string representation */
public String toString(ComparisonFormatter formatter);
}
public static class Detail {
/** Get the target node being compared */
public Node getTarget();
/** Get XPath to the target node */
public String getXPath();
/** Get the value being compared */
public Object getValue();
/** Get XPath to the parent node */
public String getParentXPath();
}Enumerations defining what is being compared and the outcome.
public enum ComparisonType {
ATTR_NAME_LOOKUP, ATTR_VALUE, CHILD_LOOKUP, CHILD_NODELIST_LENGTH,
CHILD_NODELIST_SEQUENCE, ELEMENT_NAME, ELEMENT_TAG_NAME, HAS_DOCTYPE_DECLARATION,
NAMESPACE_PREFIX, NAMESPACE_URI, NODE_TYPE, NO_NAMESPACE_SCHEMA_LOCATION,
PROCESSING_INSTRUCTION_DATA, PROCESSING_INSTRUCTION_TARGET, SCHEMA_LOCATION,
TEXT_VALUE, XML_VERSION, XML_STANDALONE, XML_ENCODING, DOCTYPE_NAME,
DOCTYPE_PUBLIC_ID, DOCTYPE_SYSTEM_ID, COMMENT_VALUE, CDATA_VALUE
}
public enum ComparisonResult {
/** Content is identical */
EQUAL,
/** Content is similar but not identical */
SIMILAR,
/** Content is completely different */
DIFFERENT
}Configure how control and test nodes are matched during comparison.
public interface NodeMatcher {
/** Match child nodes between control and test parents */
Iterable<Map.Entry<Node, Node>> match(Iterable<Node> controlNodes,
Iterable<Node> testNodes);
}
public class DefaultNodeMatcher implements NodeMatcher {
/** Create matcher with element selector strategies */
public DefaultNodeMatcher(ElementSelector... selectors);
}
// Built-in element selectors
public class ElementSelectors {
public static final ElementSelector byName;
public static final ElementSelector byNameAndText;
public static final ElementSelector byNameAndAllAttributes;
public static final ElementSelector byNameAndAttributes(String... attributeNames);
/** Chain multiple selectors with fallback behavior */
public static ElementSelector conditionalBuilder()
.whenElementIsNamed(String expectedName)
.thenUse(ElementSelector selector)
.elseUse(ElementSelector selector)
.build();
}Customize how comparison results are interpreted and handled.
public interface DifferenceEvaluator {
/** Evaluate a comparison and potentially change its result */
ComparisonResult evaluate(Comparison comparison, ComparisonResult outcome);
}
// Built-in evaluators
public class DifferenceEvaluators {
/** Default evaluator that accepts all results as-is */
public static final DifferenceEvaluator Default;
/** Ignore specific comparison types */
public static DifferenceEvaluator ignorePaths(String... xPaths);
/** Chain multiple evaluators */
public static DifferenceEvaluator chain(DifferenceEvaluator... evaluators);
/** Upgrade SIMILAR results to EQUAL */
public static final DifferenceEvaluator upgradeDifferencesToEqual;
/** Downgrade DIFFERENT results to SIMILAR */
public static final DifferenceEvaluator downgradeDifferencesToSimilar;
}Usage Examples:
// Ignore attribute order differences
DifferenceEvaluator ignoreAttrSequence = (comparison, outcome) -> {
if (comparison.getType() == ComparisonType.ATTR_SEQUENCE) {
return ComparisonResult.SIMILAR;
}
return outcome;
};
// Chain multiple evaluators
DifferenceEvaluator chained = DifferenceEvaluators.chain(
ignoreAttrSequence,
DifferenceEvaluators.ignorePaths("/users/user/@created"),
DifferenceEvaluators.downgradeDifferencesToSimilar
);
Diff customEvaluatedDiff = DiffBuilder.compare(control)
.withTest(test)
.withDifferenceEvaluator(chained)
.build();Customize how comparison results are displayed and reported.
public interface ComparisonFormatter {
/** Format comparison details for display */
String getDescription(Comparison difference);
/** Get short summary of comparison */
String getDetails(Detail details, ComparisonType type, boolean formatting);
}
public class DefaultComparisonFormatter implements ComparisonFormatter {
/** Default formatting implementation */
}Direct access to the comparison engine for advanced use cases.
public interface DifferenceEngine {
/** Add listener for comparison events */
void addDifferenceListener(ComparisonListener listener);
/** Add listener that can control comparison flow */
void addComparisonController(ComparisonController controller);
/** Add listener for completed comparisons */
void addMatchListener(ComparisonListener listener);
/** Perform comparison between control and test sources */
void compare(Source control, Source test);
}
public class DOMDifferenceEngine extends AbstractDifferenceEngine {
/** DOM-based difference engine implementation */
}
public interface ComparisonListener {
/** Called for each comparison performed */
void comparisonPerformed(Comparison comparison, ComparisonResult outcome);
}
public interface ComparisonController extends ComparisonListener {
/** Control whether comparison should continue */
boolean stopDiffing(Comparison comparison);
}Usage Examples:
// Direct engine usage with listeners
DOMDifferenceEngine engine = new DOMDifferenceEngine();
engine.addDifferenceListener((comparison, outcome) -> {
if (outcome == ComparisonResult.DIFFERENT) {
System.out.println("Found difference: " + comparison);
}
});
engine.addComparisonController(new ComparisonController() {
private int differenceCount = 0;
@Override
public void comparisonPerformed(Comparison comparison, ComparisonResult outcome) {
if (outcome != ComparisonResult.EQUAL) {
differenceCount++;
}
}
@Override
public boolean stopDiffing(Comparison comparison) {
// Stop after finding 10 differences
return differenceCount >= 10;
}
});
engine.compare(controlSource, testSource);Install with Tessl CLI
npx tessl i tessl/maven-org-xmlunit--xmlunit-core