0
# Custom Value Matching
1
2
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.
3
4
## Capabilities
5
6
### Value Matcher Interface
7
8
Core interface for implementing custom value comparison logic.
9
10
```java { .api }
11
public interface ValueMatcher<T> {
12
/**
13
* Compares two objects for equality using custom logic.
14
*
15
* @param o1 the first object to check
16
* @param o2 the object to check the first against
17
* @return true if the objects are equal, false otherwise
18
*/
19
boolean equal(T o1, T o2);
20
}
21
```
22
23
**Usage Examples:**
24
25
```java
26
// Custom matcher for case-insensitive string comparison
27
ValueMatcher<Object> caseInsensitiveMatcher = new ValueMatcher<Object>() {
28
@Override
29
public boolean equal(Object o1, Object o2) {
30
if (o1 instanceof String && o2 instanceof String) {
31
return ((String) o1).equalsIgnoreCase((String) o2);
32
}
33
return Objects.equals(o1, o2);
34
}
35
};
36
37
// Use with customization
38
Customization customization = new Customization("user.name", caseInsensitiveMatcher);
39
CustomComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, customization);
40
JSONAssert.assertEquals(expected, actual, comparator);
41
```
42
43
### Location-Aware Value Matcher
44
45
Extended interface providing context about the JSON path being compared and access to the comparison result for advanced error handling.
46
47
```java { .api }
48
public interface LocationAwareValueMatcher<T> extends ValueMatcher<T> {
49
/**
50
* Compare values with location context and result access.
51
*
52
* @param prefix JSON path of the JSON item being tested
53
* @param actual JSON value being tested
54
* @param expected expected JSON value
55
* @param result JSONCompareResult to which match failure may be passed
56
* @return true if expected and actual equal or difference already passed to result
57
* @throws ValueMatcherException if custom error message needed
58
*/
59
boolean equal(String prefix, T actual, T expected, JSONCompareResult result) throws ValueMatcherException;
60
}
61
```
62
63
### Regular Expression Matcher
64
65
Matches string values against regular expression patterns with support for both static and dynamic patterns.
66
67
```java { .api }
68
public class RegularExpressionValueMatcher<T> implements ValueMatcher<T> {
69
/**
70
* Create matcher with dynamic pattern from expected value.
71
*/
72
public RegularExpressionValueMatcher();
73
74
/**
75
* Create matcher with constant pattern.
76
*
77
* @param pattern regular expression pattern, null for dynamic mode
78
* @throws IllegalArgumentException if pattern is invalid
79
*/
80
public RegularExpressionValueMatcher(String pattern) throws IllegalArgumentException;
81
82
public boolean equal(T actual, T expected);
83
}
84
```
85
86
**Usage Examples:**
87
88
```java
89
// Static pattern - all values must match this regex
90
RegularExpressionValueMatcher<Object> emailMatcher =
91
new RegularExpressionValueMatcher<>("^[\\w._%+-]+@[\\w.-]+\\.[A-Z]{2,}$");
92
93
// Dynamic pattern - expected value contains the regex
94
RegularExpressionValueMatcher<Object> dynamicMatcher =
95
new RegularExpressionValueMatcher<>();
96
97
// Use with customization for email validation
98
Customization emailCustomization = new Customization("user.email", emailMatcher);
99
CustomComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, emailCustomization);
100
101
// Test with dynamic pattern
102
String expected = "{\"id\":\"\\\\d+\"}"; // Expected contains regex pattern
103
String actual = "{\"id\":\"12345\"}"; // Actual contains value to match
104
JSONAssert.assertEquals(expected, actual,
105
new CustomComparator(JSONCompareMode.LENIENT,
106
new Customization("id", dynamicMatcher)));
107
```
108
109
### Array Value Matcher
110
111
Specialized matcher for arrays that provides flexible element-by-element comparison with support for partial matching, index-specific validation, and repeating patterns.
112
113
```java { .api }
114
public class ArrayValueMatcher<T> implements LocationAwareValueMatcher<T> {
115
/**
116
* Match every element in actual array against expected array pattern.
117
*
118
* @param comparator comparator to use for element comparison
119
*/
120
public ArrayValueMatcher(JSONComparator comparator);
121
122
/**
123
* Match specific element in actual array against first element of expected.
124
*
125
* @param comparator comparator to use for element comparison
126
* @param index index of the array element to be compared
127
*/
128
public ArrayValueMatcher(JSONComparator comparator, int index);
129
130
/**
131
* Match elements in specified range against expected array pattern.
132
*
133
* @param comparator comparator to use for element comparison
134
* @param from first element index to compare (inclusive)
135
* @param to last element index to compare (inclusive)
136
*/
137
public ArrayValueMatcher(JSONComparator comparator, int from, int to);
138
139
public boolean equal(T o1, T o2);
140
public boolean equal(String prefix, T actual, T expected, JSONCompareResult result);
141
}
142
```
143
144
**Usage Examples:**
145
146
```java
147
// Validate all array elements have same structure
148
JSONComparator elementComparator = new DefaultComparator(JSONCompareMode.LENIENT);
149
ArrayValueMatcher<Object> allElementsMatcher = new ArrayValueMatcher<>(elementComparator);
150
151
// Expected pattern (will be repeated for each actual element)
152
String expected = "{\"users\":[{\"active\":true}]}";
153
String actual = "{\"users\":[{\"active\":true,\"id\":1},{\"active\":true,\"id\":2}]}";
154
155
Customization arrayCustomization = new Customization("users", allElementsMatcher);
156
CustomComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, arrayCustomization);
157
JSONAssert.assertEquals(expected, actual, comparator);
158
159
// Validate specific array element
160
ArrayValueMatcher<Object> firstElementMatcher = new ArrayValueMatcher<>(elementComparator, 0);
161
Customization firstElementCustomization = new Customization("items", firstElementMatcher);
162
163
// Validate range of elements
164
ArrayValueMatcher<Object> rangeMatcher = new ArrayValueMatcher<>(elementComparator, 1, 3);
165
Customization rangeCustomization = new Customization("products", rangeMatcher);
166
167
// Complex array validation with regex
168
int arrayLength = 4; // known array length
169
RegularExpressionValueMatcher<Object> idMatcher = new RegularExpressionValueMatcher<>("\\\\d+");
170
Customization[] idCustomizations = new Customization[arrayLength];
171
for (int i = 0; i < arrayLength; i++) {
172
idCustomizations[i] = new Customization("items[" + i + "].id", idMatcher);
173
}
174
CustomComparator idComparator = new CustomComparator(JSONCompareMode.STRICT_ORDER, idCustomizations);
175
ArrayValueMatcher<Object> idArrayMatcher = new ArrayValueMatcher<>(idComparator);
176
```
177
178
### Path-Based Customization
179
180
Associates custom matchers with specific JSON paths using pattern matching with wildcards and path expressions.
181
182
```java { .api }
183
public class Customization {
184
/**
185
* Create customization for specific JSON path.
186
*
187
* @param path JSON path pattern (supports *, **, wildcards)
188
* @param comparator value matcher for this path
189
*/
190
public Customization(String path, ValueMatcher<Object> comparator);
191
192
/**
193
* Factory method for creating customizations.
194
*/
195
public static Customization customization(String path, ValueMatcher<Object> comparator);
196
197
/**
198
* Check if customization applies to given path.
199
*/
200
public boolean appliesToPath(String path);
201
202
/**
203
* Test if values match using this customization's matcher.
204
*
205
* @deprecated Use matches(String, Object, Object, JSONCompareResult)
206
*/
207
@Deprecated
208
public boolean matches(Object actual, Object expected);
209
210
/**
211
* Test values with location awareness and result reporting.
212
*/
213
public boolean matches(String prefix, Object actual, Object expected, JSONCompareResult result)
214
throws ValueMatcherException;
215
}
216
```
217
218
**Path Pattern Examples:**
219
220
```java
221
// Exact path match
222
new Customization("user.name", matcher);
223
224
// Wildcard for any single field name
225
new Customization("users.*.active", matcher);
226
227
// Double wildcard for any nested path
228
new Customization("**.email", matcher);
229
230
// Array element patterns
231
new Customization("items[0].id", matcher);
232
new Customization("users[*].roles[*]", matcher);
233
234
// Complex patterns
235
new Customization("data.**.validation.*.result", matcher);
236
```
237
238
### Value Matcher Exception
239
240
Exception for custom error messages from value matchers.
241
242
```java { .api }
243
public class ValueMatcherException extends RuntimeException {
244
public ValueMatcherException(String message, String expected, String actual);
245
public ValueMatcherException(String message, Throwable cause, String expected, String actual);
246
247
public String getExpected();
248
public String getActual();
249
}
250
```
251
252
**Usage Examples:**
253
254
```java
255
ValueMatcher<Object> rangeValidator = new ValueMatcher<Object>() {
256
@Override
257
public boolean equal(Object actual, Object expected) {
258
if (actual instanceof Number && expected instanceof Number) {
259
double actualVal = ((Number) actual).doubleValue();
260
double expectedVal = ((Number) expected).doubleValue();
261
262
if (Math.abs(actualVal - expectedVal) > 0.01) {
263
throw new ValueMatcherException(
264
"Values differ by more than 0.01",
265
expected, actual
266
);
267
}
268
return true;
269
}
270
return Objects.equals(actual, expected);
271
}
272
};
273
```
274
275
## Advanced Usage Patterns
276
277
### Combining Multiple Customizations
278
279
```java
280
// Multiple field-specific matchers
281
Customization emailCustomization = new Customization("**.email",
282
new RegularExpressionValueMatcher<>("^[\\w._%+-]+@[\\w.-]+\\.[A-Z]{2,}$"));
283
284
Customization phoneCustomization = new Customization("**.phone",
285
new RegularExpressionValueMatcher<>("^\\+?[1-9]\\d{1,14}$"));
286
287
Customization nameCustomization = new Customization("**.name",
288
new ValueMatcher<Object>() {
289
public boolean equal(Object o1, Object o2) {
290
return o1.toString().equalsIgnoreCase(o2.toString());
291
}
292
});
293
294
CustomComparator multiCustomComparator = new CustomComparator(
295
JSONCompareMode.LENIENT,
296
emailCustomization,
297
phoneCustomization,
298
nameCustomization
299
);
300
```
301
302
### Array Element Validation
303
304
```java
305
// Validate array structure with different matchers per field
306
DefaultComparator baseComparator = new DefaultComparator(JSONCompareMode.LENIENT);
307
308
// Create customizations for ID validation within array elements
309
RegularExpressionValueMatcher<Object> uuidMatcher =
310
new RegularExpressionValueMatcher<>("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");
311
312
Customization idValidation = new Customization("items.*.id", uuidMatcher);
313
CustomComparator elementComparator = new CustomComparator(JSONCompareMode.LENIENT, idValidation);
314
315
// Apply to all array elements
316
ArrayValueMatcher<Object> arrayMatcher = new ArrayValueMatcher<>(elementComparator);
317
Customization arrayCustomization = new Customization("items", arrayMatcher);
318
319
CustomComparator finalComparator = new CustomComparator(JSONCompareMode.LENIENT, arrayCustomization);
320
321
String expected = "{\"items\":[{\"id\":\"PLACEHOLDER\",\"name\":\"test\"}]}";
322
String actual = "{\"items\":[{\"id\":\"550e8400-e29b-41d4-a716-446655440000\",\"name\":\"test\"}]}";
323
324
JSONAssert.assertEquals(expected, actual, finalComparator);
325
```