or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mdcomparators.mdcomparison.mdcustom-matchers.mdindex.md

comparators.mddocs/

0

# JSON Comparators

1

2

Pluggable comparison strategies including default comparison logic, custom field-level matching, and specialized array size comparison for different validation scenarios. The comparator system provides the core comparison engine that can be extended and customized for specific testing needs.

3

4

## Capabilities

5

6

### JSONComparator Interface

7

8

Core interface defining the contract for JSON comparison implementations.

9

10

```java { .api }

11

public interface JSONComparator {

12

/**

13

* Compare two JSONObjects and return detailed results.

14

*/

15

JSONCompareResult compareJSON(JSONObject expected, JSONObject actual) throws JSONException;

16

17

/**

18

* Compare two JSONArrays and return detailed results.

19

*/

20

JSONCompareResult compareJSON(JSONArray expected, JSONArray actual) throws JSONException;

21

22

/**

23

* Compare JSONObjects with path context, updating result in-place.

24

*/

25

void compareJSON(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) throws JSONException;

26

27

/**

28

* Compare individual values with path context, updating result in-place.

29

*/

30

void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException;

31

32

/**

33

* Compare JSONArrays with path context, updating result in-place.

34

*/

35

void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result) throws JSONException;

36

}

37

```

38

39

### Default Comparator

40

41

Standard implementation providing configurable comparison behavior based on JSONCompareMode settings.

42

43

```java { .api }

44

public class DefaultComparator implements JSONComparator {

45

/**

46

* Create comparator with specified comparison mode.

47

*/

48

public DefaultComparator(JSONCompareMode mode);

49

50

public JSONCompareResult compareJSON(JSONObject expected, JSONObject actual) throws JSONException;

51

public JSONCompareResult compareJSON(JSONArray expected, JSONArray actual) throws JSONException;

52

public void compareJSON(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) throws JSONException;

53

public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException;

54

public void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result) throws JSONException;

55

}

56

```

57

58

**Usage Examples:**

59

60

```java

61

// Create comparators with different modes

62

DefaultComparator strictComparator = new DefaultComparator(JSONCompareMode.STRICT);

63

DefaultComparator lenientComparator = new DefaultComparator(JSONCompareMode.LENIENT);

64

65

// Use directly for programmatic comparison

66

JSONCompareResult result = strictComparator.compareJSON(expectedObject, actualObject);

67

68

// Use with JSONAssert

69

JSONAssert.assertEquals(expected, actual, lenientComparator);

70

```

71

72

### Custom Comparator

73

74

Extends DefaultComparator to support field-specific custom matching through Customization objects.

75

76

```java { .api }

77

public class CustomComparator extends DefaultComparator {

78

/**

79

* Create custom comparator with base mode and field customizations.

80

*

81

* @param mode base comparison mode for non-customized fields

82

* @param customizations variable number of field-specific customizations

83

*/

84

public CustomComparator(JSONCompareMode mode, Customization... customizations);

85

86

@Override

87

public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException;

88

}

89

```

90

91

**Usage Examples:**

92

93

```java

94

// Single customization

95

RegularExpressionValueMatcher<Object> emailMatcher =

96

new RegularExpressionValueMatcher<>("^[\\w._%+-]+@[\\w.-]+\\.[A-Z]{2,}$");

97

Customization emailCustomization = new Customization("user.email", emailMatcher);

98

CustomComparator emailComparator = new CustomComparator(JSONCompareMode.LENIENT, emailCustomization);

99

100

// Multiple customizations

101

Customization timestampCustomization = new Customization("**.timestamp",

102

new RegularExpressionValueMatcher<>("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"));

103

104

Customization idCustomization = new Customization("**.id",

105

new RegularExpressionValueMatcher<>("^\\d+$"));

106

107

CustomComparator multiCustomComparator = new CustomComparator(

108

JSONCompareMode.LENIENT,

109

emailCustomization,

110

timestampCustomization,

111

idCustomization

112

);

113

114

// Use with assertions

115

JSONAssert.assertEquals(expected, actual, multiCustomComparator);

116

```

117

118

### Abstract Comparator

119

120

Base class providing common functionality for comparator implementations.

121

122

```java { .api }

123

public abstract class AbstractComparator implements JSONComparator {

124

/**

125

* Protected constructor for subclasses.

126

*/

127

protected AbstractComparator();

128

129

// Provides default implementations of interface methods

130

// that can be overridden by subclasses

131

}

132

```

133

134

### Array Size Comparator

135

136

Specialized comparator that compares arrays by size rather than content, useful for validating array length constraints.

137

138

```java { .api }

139

public class ArraySizeComparator extends DefaultComparator {

140

/**

141

* Create array size comparator with specified mode.

142

*/

143

public ArraySizeComparator(JSONCompareMode mode);

144

145

// Compares arrays by length instead of element-by-element comparison

146

}

147

```

148

149

**Usage Examples:**

150

151

```java

152

// Validate that arrays have expected lengths

153

ArraySizeComparator sizeComparator = new ArraySizeComparator(JSONCompareMode.STRICT_ORDER);

154

155

// Expected array length is encoded in the expected JSON

156

String expected = "{\"items\":[1,2,3]}"; // Expects 3 elements

157

String actual = "{\"items\":[\"a\",\"b\",\"c\"]}"; // Has 3 elements

158

159

JSONAssert.assertEquals(expected, actual, sizeComparator); // Passes - same length

160

161

// Use with array value matcher for partial size validation

162

ArrayValueMatcher<Object> sizeArrayMatcher = new ArrayValueMatcher<>(sizeComparator, 0, 2);

163

Customization sizeCustomization = new Customization("data.arrays.*", sizeArrayMatcher);

164

```

165

166

### JSONCompare Utility

167

168

Utility class providing convenience methods for JSON comparison operations.

169

170

```java { .api }

171

public class JSONCompareUtil {

172

// Static utility methods for common comparison operations

173

// (Implementation details vary - check source for specific methods)

174

}

175

```

176

177

## Advanced Comparator Patterns

178

179

### Custom Comparator Implementation

180

181

```java

182

// Example: Case-insensitive string comparator

183

public class CaseInsensitiveComparator extends DefaultComparator {

184

public CaseInsensitiveComparator(JSONCompareMode mode) {

185

super(mode);

186

}

187

188

@Override

189

public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException {

190

if (expectedValue instanceof String && actualValue instanceof String) {

191

String expectedStr = (String) expectedValue;

192

String actualStr = (String) actualValue;

193

194

if (!expectedStr.equalsIgnoreCase(actualStr)) {

195

result.fail(prefix, expectedValue, actualValue);

196

}

197

} else {

198

super.compareValues(prefix, expectedValue, actualValue, result);

199

}

200

}

201

}

202

203

// Usage

204

CaseInsensitiveComparator caseInsensitive = new CaseInsensitiveComparator(JSONCompareMode.LENIENT);

205

JSONAssert.assertEquals("{\"name\":\"john\"}", "{\"name\":\"JOHN\"}", caseInsensitive);

206

```

207

208

### Nested Custom Comparators

209

210

```java

211

// Complex validation with nested customizations

212

public void validateComplexJson() throws JSONException {

213

// Inner comparator for validating array elements

214

RegularExpressionValueMatcher<Object> idMatcher = new RegularExpressionValueMatcher<>("^ID-\\d+$");

215

Customization innerIdCustomization = new Customization("id", idMatcher);

216

CustomComparator innerComparator = new CustomComparator(JSONCompareMode.LENIENT, innerIdCustomization);

217

218

// Array matcher using the inner comparator

219

ArrayValueMatcher<Object> arrayMatcher = new ArrayValueMatcher<>(innerComparator);

220

Customization arrayCustomization = new Customization("users", arrayMatcher);

221

222

// Outer comparator with email validation and array validation

223

RegularExpressionValueMatcher<Object> emailMatcher = new RegularExpressionValueMatcher<>("^[\\w._%+-]+@[\\w.-]+\\.[A-Z]{2,}$");

224

Customization emailCustomization = new Customization("contact.email", emailMatcher);

225

226

CustomComparator outerComparator = new CustomComparator(

227

JSONCompareMode.LENIENT,

228

arrayCustomization,

229

emailCustomization

230

);

231

232

String expected = """

233

{

234

"contact": {"email": "user@example.com"},

235

"users": [{"id": "ID-PLACEHOLDER", "name": "test"}]

236

}""";

237

238

String actual = """

239

{

240

"contact": {"email": "user@example.com"},

241

"users": [

242

{"id": "ID-123", "name": "test", "active": true},

243

{"id": "ID-456", "name": "test", "role": "admin"}

244

]

245

}""";

246

247

JSONAssert.assertEquals(expected, actual, outerComparator);

248

}

249

```

250

251

### Combining Different Comparison Strategies

252

253

```java

254

// Multi-strategy validation approach

255

public class HybridComparator extends DefaultComparator {

256

private final ArraySizeComparator sizeComparator;

257

private final CustomComparator customComparator;

258

259

public HybridComparator(JSONCompareMode mode, Customization... customizations) {

260

super(mode);

261

this.sizeComparator = new ArraySizeComparator(mode);

262

this.customComparator = new CustomComparator(mode, customizations);

263

}

264

265

@Override

266

public void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result) throws JSONException {

267

// First check array sizes

268

if (prefix.endsWith(".sizes")) {

269

sizeComparator.compareJSONArray(prefix, expected, actual, result);

270

} else if (hasCustomization(prefix)) {

271

customComparator.compareJSONArray(prefix, expected, actual, result);

272

} else {

273

super.compareJSONArray(prefix, expected, actual, result);

274

}

275

}

276

277

private boolean hasCustomization(String prefix) {

278

// Logic to determine if path has customizations

279

return customComparator.hasCustomizationForPath(prefix);

280

}

281

}

282

```

283

284

### Error Handling in Custom Comparators

285

286

```java

287

public class ValidatingComparator extends DefaultComparator {

288

public ValidatingComparator(JSONCompareMode mode) {

289

super(mode);

290

}

291

292

@Override

293

public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException {

294

try {

295

// Custom validation logic

296

if (prefix.contains("date")) {

297

validateDateFormat(expectedValue, actualValue, result, prefix);

298

} else if (prefix.contains("url")) {

299

validateUrlFormat(expectedValue, actualValue, result, prefix);

300

} else {

301

super.compareValues(prefix, expectedValue, actualValue, result);

302

}

303

} catch (Exception e) {

304

result.fail(prefix + " validation error: " + e.getMessage());

305

}

306

}

307

308

private void validateDateFormat(Object expected, Object actual, JSONCompareResult result, String prefix) {

309

// Custom date validation logic

310

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

311

try {

312

dateFormat.parse(actual.toString());

313

// Date is valid, consider it a match regardless of expected value

314

} catch (ParseException e) {

315

result.fail(prefix, "valid date format", actual);

316

}

317

}

318

319

private void validateUrlFormat(Object expected, Object actual, JSONCompareResult result, String prefix) {

320

try {

321

new URL(actual.toString());

322

// URL is valid

323

} catch (MalformedURLException e) {

324

result.fail(prefix, "valid URL format", actual);

325

}

326

}

327

}

328

```

329

330

## Integration Points

331

332

### With JSONAssert

333

334

```java

335

// All assertion methods accept JSONComparator

336

JSONAssert.assertEquals(expected, actual, customComparator);

337

JSONAssert.assertNotEquals(expected, actual, customComparator);

338

```

339

340

### With JSONCompare

341

342

```java

343

// Direct use with comparison methods

344

JSONCompareResult result = JSONCompare.compareJSON(expected, actual, customComparator);

345

```

346

347

### Factory Pattern

348

349

```java

350

public class ComparatorFactory {

351

public static JSONComparator createEmailValidatingComparator() {

352

RegularExpressionValueMatcher<Object> emailMatcher =

353

new RegularExpressionValueMatcher<>("^[\\w._%+-]+@[\\w.-]+\\.[A-Z]{2,}$");

354

Customization emailCustomization = new Customization("**.email", emailMatcher);

355

return new CustomComparator(JSONCompareMode.LENIENT, emailCustomization);

356

}

357

358

public static JSONComparator createStrictArrayComparator() {

359

return new DefaultComparator(JSONCompareMode.STRICT_ORDER);

360

}

361

362

public static JSONComparator createSizeOnlyComparator() {

363

return new ArraySizeComparator(JSONCompareMode.LENIENT);

364

}

365

}

366

```