CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-skyscreamer--jsonassert

Write JSON unit tests in less code. Great for testing REST interfaces.

Pending
Overview
Eval results
Files

custom-matchers.mddocs/

Custom Value Matching

Advanced validation system enabling custom field-level matching with regular expressions, array validation, and path-based customization for complex JSON structures. This system allows you to define custom comparison logic for specific JSON paths or value types.

Capabilities

Value Matcher Interface

Core interface for implementing custom value comparison logic.

public interface ValueMatcher<T> {
    /**
     * Compares two objects for equality using custom logic.
     * 
     * @param o1 the first object to check
     * @param o2 the object to check the first against  
     * @return true if the objects are equal, false otherwise
     */
    boolean equal(T o1, T o2);
}

Usage Examples:

// Custom matcher for case-insensitive string comparison
ValueMatcher<Object> caseInsensitiveMatcher = new ValueMatcher<Object>() {
    @Override
    public boolean equal(Object o1, Object o2) {
        if (o1 instanceof String && o2 instanceof String) {
            return ((String) o1).equalsIgnoreCase((String) o2);
        }
        return Objects.equals(o1, o2);
    }
};

// Use with customization
Customization customization = new Customization("user.name", caseInsensitiveMatcher);
CustomComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, customization);
JSONAssert.assertEquals(expected, actual, comparator);

Location-Aware Value Matcher

Extended interface providing context about the JSON path being compared and access to the comparison result for advanced error handling.

public interface LocationAwareValueMatcher<T> extends ValueMatcher<T> {
    /**
     * Compare values with location context and result access.
     * 
     * @param prefix JSON path of the JSON item being tested
     * @param actual JSON value being tested
     * @param expected expected JSON value
     * @param result JSONCompareResult to which match failure may be passed
     * @return true if expected and actual equal or difference already passed to result
     * @throws ValueMatcherException if custom error message needed
     */
    boolean equal(String prefix, T actual, T expected, JSONCompareResult result) throws ValueMatcherException;
}

Regular Expression Matcher

Matches string values against regular expression patterns with support for both static and dynamic patterns.

public class RegularExpressionValueMatcher<T> implements ValueMatcher<T> {
    /**
     * Create matcher with dynamic pattern from expected value.
     */
    public RegularExpressionValueMatcher();
    
    /**
     * Create matcher with constant pattern.
     * 
     * @param pattern regular expression pattern, null for dynamic mode
     * @throws IllegalArgumentException if pattern is invalid
     */
    public RegularExpressionValueMatcher(String pattern) throws IllegalArgumentException;
    
    public boolean equal(T actual, T expected);
}

Usage Examples:

// Static pattern - all values must match this regex
RegularExpressionValueMatcher<Object> emailMatcher = 
    new RegularExpressionValueMatcher<>("^[\\w._%+-]+@[\\w.-]+\\.[A-Z]{2,}$");

// Dynamic pattern - expected value contains the regex
RegularExpressionValueMatcher<Object> dynamicMatcher = 
    new RegularExpressionValueMatcher<>();

// Use with customization for email validation
Customization emailCustomization = new Customization("user.email", emailMatcher);
CustomComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, emailCustomization);

// Test with dynamic pattern
String expected = "{\"id\":\"\\\\d+\"}"; // Expected contains regex pattern
String actual = "{\"id\":\"12345\"}";    // Actual contains value to match
JSONAssert.assertEquals(expected, actual, 
    new CustomComparator(JSONCompareMode.LENIENT, 
        new Customization("id", dynamicMatcher)));

Array Value Matcher

Specialized matcher for arrays that provides flexible element-by-element comparison with support for partial matching, index-specific validation, and repeating patterns.

public class ArrayValueMatcher<T> implements LocationAwareValueMatcher<T> {
    /**
     * Match every element in actual array against expected array pattern.
     * 
     * @param comparator comparator to use for element comparison
     */
    public ArrayValueMatcher(JSONComparator comparator);
    
    /**
     * Match specific element in actual array against first element of expected.
     * 
     * @param comparator comparator to use for element comparison
     * @param index index of the array element to be compared
     */
    public ArrayValueMatcher(JSONComparator comparator, int index);
    
    /**
     * Match elements in specified range against expected array pattern.
     * 
     * @param comparator comparator to use for element comparison
     * @param from first element index to compare (inclusive)
     * @param to last element index to compare (inclusive)
     */
    public ArrayValueMatcher(JSONComparator comparator, int from, int to);
    
    public boolean equal(T o1, T o2);
    public boolean equal(String prefix, T actual, T expected, JSONCompareResult result);
}

Usage Examples:

// Validate all array elements have same structure
JSONComparator elementComparator = new DefaultComparator(JSONCompareMode.LENIENT);
ArrayValueMatcher<Object> allElementsMatcher = new ArrayValueMatcher<>(elementComparator);

// Expected pattern (will be repeated for each actual element)
String expected = "{\"users\":[{\"active\":true}]}";
String actual = "{\"users\":[{\"active\":true,\"id\":1},{\"active\":true,\"id\":2}]}";

Customization arrayCustomization = new Customization("users", allElementsMatcher);
CustomComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, arrayCustomization);
JSONAssert.assertEquals(expected, actual, comparator);

// Validate specific array element
ArrayValueMatcher<Object> firstElementMatcher = new ArrayValueMatcher<>(elementComparator, 0);
Customization firstElementCustomization = new Customization("items", firstElementMatcher);

// Validate range of elements  
ArrayValueMatcher<Object> rangeMatcher = new ArrayValueMatcher<>(elementComparator, 1, 3);
Customization rangeCustomization = new Customization("products", rangeMatcher);

// Complex array validation with regex
int arrayLength = 4; // known array length
RegularExpressionValueMatcher<Object> idMatcher = new RegularExpressionValueMatcher<>("\\\\d+");
Customization[] idCustomizations = new Customization[arrayLength];
for (int i = 0; i < arrayLength; i++) {
    idCustomizations[i] = new Customization("items[" + i + "].id", idMatcher);
}
CustomComparator idComparator = new CustomComparator(JSONCompareMode.STRICT_ORDER, idCustomizations);
ArrayValueMatcher<Object> idArrayMatcher = new ArrayValueMatcher<>(idComparator);

Path-Based Customization

Associates custom matchers with specific JSON paths using pattern matching with wildcards and path expressions.

public class Customization {
    /**
     * Create customization for specific JSON path.
     * 
     * @param path JSON path pattern (supports *, **, wildcards)
     * @param comparator value matcher for this path
     */
    public Customization(String path, ValueMatcher<Object> comparator);
    
    /**
     * Factory method for creating customizations.
     */
    public static Customization customization(String path, ValueMatcher<Object> comparator);
    
    /**
     * Check if customization applies to given path.
     */
    public boolean appliesToPath(String path);
    
    /**
     * Test if values match using this customization's matcher.
     * 
     * @deprecated Use matches(String, Object, Object, JSONCompareResult)
     */
    @Deprecated
    public boolean matches(Object actual, Object expected);
    
    /**
     * Test values with location awareness and result reporting.
     */
    public boolean matches(String prefix, Object actual, Object expected, JSONCompareResult result) 
        throws ValueMatcherException;
}

Path Pattern Examples:

// Exact path match
new Customization("user.name", matcher);

// Wildcard for any single field name
new Customization("users.*.active", matcher);

// Double wildcard for any nested path
new Customization("**.email", matcher);

// Array element patterns
new Customization("items[0].id", matcher);
new Customization("users[*].roles[*]", matcher);

// Complex patterns
new Customization("data.**.validation.*.result", matcher);

Value Matcher Exception

Exception for custom error messages from value matchers.

public class ValueMatcherException extends RuntimeException {
    public ValueMatcherException(String message, String expected, String actual);
    public ValueMatcherException(String message, Throwable cause, String expected, String actual);
    
    public String getExpected();
    public String getActual();
}

Usage Examples:

ValueMatcher<Object> rangeValidator = new ValueMatcher<Object>() {
    @Override
    public boolean equal(Object actual, Object expected) {
        if (actual instanceof Number && expected instanceof Number) {
            double actualVal = ((Number) actual).doubleValue();
            double expectedVal = ((Number) expected).doubleValue();
            
            if (Math.abs(actualVal - expectedVal) > 0.01) {
                throw new ValueMatcherException(
                    "Values differ by more than 0.01", 
                    expected, actual
                );
            }
            return true;
        }
        return Objects.equals(actual, expected);
    }
};

Advanced Usage Patterns

Combining Multiple Customizations

// Multiple field-specific matchers
Customization emailCustomization = new Customization("**.email", 
    new RegularExpressionValueMatcher<>("^[\\w._%+-]+@[\\w.-]+\\.[A-Z]{2,}$"));

Customization phoneCustomization = new Customization("**.phone", 
    new RegularExpressionValueMatcher<>("^\\+?[1-9]\\d{1,14}$"));

Customization nameCustomization = new Customization("**.name", 
    new ValueMatcher<Object>() {
        public boolean equal(Object o1, Object o2) {
            return o1.toString().equalsIgnoreCase(o2.toString());
        }
    });

CustomComparator multiCustomComparator = new CustomComparator(
    JSONCompareMode.LENIENT, 
    emailCustomization, 
    phoneCustomization, 
    nameCustomization
);

Array Element Validation

// Validate array structure with different matchers per field
DefaultComparator baseComparator = new DefaultComparator(JSONCompareMode.LENIENT);

// Create customizations for ID validation within array elements
RegularExpressionValueMatcher<Object> uuidMatcher = 
    new RegularExpressionValueMatcher<>("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");

Customization idValidation = new Customization("items.*.id", uuidMatcher);
CustomComparator elementComparator = new CustomComparator(JSONCompareMode.LENIENT, idValidation);

// Apply to all array elements
ArrayValueMatcher<Object> arrayMatcher = new ArrayValueMatcher<>(elementComparator);
Customization arrayCustomization = new Customization("items", arrayMatcher);

CustomComparator finalComparator = new CustomComparator(JSONCompareMode.LENIENT, arrayCustomization);

String expected = "{\"items\":[{\"id\":\"PLACEHOLDER\",\"name\":\"test\"}]}";
String actual = "{\"items\":[{\"id\":\"550e8400-e29b-41d4-a716-446655440000\",\"name\":\"test\"}]}";

JSONAssert.assertEquals(expected, actual, finalComparator);

Install with Tessl CLI

npx tessl i tessl/maven-org-skyscreamer--jsonassert

docs

assertions.md

comparators.md

comparison.md

custom-matchers.md

index.md

tile.json