
jsonassert 默认严格区分字符串形式的数字(如 "123" 和 "123.00"),但可通过调整数据类型、启用逻辑比较或自定义字段比较器实现数值语义等价校验。
jsonassert 默认严格区分字符串形式的数字(如 "123" 和 "123.00"),但可通过调整数据类型、启用逻辑比较或自定义字段比较器实现数值语义等价校验。
在使用 JSONAssert.assertEquals() 进行 JSON 结构与值校验时,若期望 "balance": "123" 与 "balance": "123.00" 被视为等价(即按数值语义而非字符串字面量比较),需明确告知 JSONAssert 执行逻辑比较(logical comparison),而非默认的严格比较(strict comparison)。
✅ 正确做法:启用逻辑比较(推荐首选)
将 strict 参数设为 true 并配合非字符串类型的 JSON 值表示,即可触发 JSONAssert 的内置数值归一化逻辑:
String expected = """
{
"id": "1234567",
"balance": 123
}
""";
String actual = """
{
"id": "1234567",
"balance": 123.00
}
""";
// 注意:第三个参数为 true → 启用 strict mode(此时 JSONAssert 会进行类型感知的逻辑比较)
JSONAssert.assertEquals("incorrect json", actual, expected, true);
⚠️ 关键点:
- expected 和 actual 中的 balance 必须写成 JSON 数字字面量(不带引号),如 123 或 123.00;
- 若写成 "123" 或 "123.00"(字符串),JSONAssert 会按字符串精确匹配,必然失败;
- 第三个参数传 true 表示 strict mode —— 此模式下,JSONAssert 会将 123(integer)与 123.00(double)视为数值相等(遵循 JSON RFC 对数字的语义定义)。
⚙️ 进阶方案:自定义字段比较器(适用于无法修改 JSON 字符串格式的场景)
当 expected 和 actual 中 balance 均为带引号的字符串(如 "123" 和 "123.00"),且你无法更改其序列化格式(例如第三方 API 返回固定字符串数字),则需通过 CustomComparator 实现字段级数值解析比较:
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.comparator.CustomComparator;
import org.skyscreamer.jsonassert.comparator.JSONComparator;
import static org.skyscreamer.jsonassert.comparator.JSONCompareMode.*;
CustomComparator comparator = new CustomComparator(
LENIENT, // 或 NON_EXTENSIBLE,取决于是否允许额外字段
new Customization("balance", (o1, o2) -> {
try {
double d1 = Double.parseDouble((String) o1);
double d2 = Double.parseDouble((String) o2);
return Double.compare(d1, d2);
} catch (NumberFormatException e) {
return ((String) o1).compareTo((String) o2); // 回退到字符串比较
}
})
);
JSONAssert.assertEquals("incorrect json", actual, expected, comparator);
? 提示:Customization 支持 XPath 风格路径(如 "$.balance"),也支持嵌套字段(如 "user.account.balance")。
? 最佳实践总结
| 场景 | 推荐方式 | 说明 |
|---|---|---|
| ✅ 可控制 JSON 样本格式 | 使用 strict=true + JSON 数字字面量 | 简洁、高效、无需额外依赖,符合 JSON 语义 |
| ⚠️ 必须保留字符串数字格式 | 使用 CustomComparator + Customization | 灵活但增加代码复杂度,建议仅用于遗留或受限接口 |
| ❌ 使用 strict=false | 不推荐 | LENIENT 模式会忽略类型差异(如 "123" vs 123),但仍不处理 "123" vs "123.00",无法满足需求 |
最后提醒:从根本上解决该问题,应确保领域模型中 balance 字段声明为 Number 类型(如 BigDecimal、Double 或 Integer),并在序列化时输出为 JSON 数字而非字符串 —— 这既是 JSON 规范的最佳实践,也能避免测试层过度妥协。










