如何用skyscreamer的JSONAssert编写特定数据类型的JSONCustomComparator?
针对特定数据类型的JSONCustomComparator实现方案
问题分析
你之前的实现依赖toString()判断值匹配,既容易出现误判(比如字符串"true"会被当成布尔值处理),也没充分利用JSONAssert的差异记录能力。下面提供两种更健壮的实现方式,既解决类型匹配需求,又能正常输出差异信息。
方案一:优化ValueMatcher+全局通配符
通过直接判断对象类型而非字符串转换,实现布尔值与整数的双向匹配,同时保留默认宽松比较逻辑,且能让JSONAssert自动生成差异报告。
import org.skyscreamer.jsonassert.JSONCompare; import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.ValueMatcher; import org.skyscreamer.jsonassert.comparator.CustomComparator; import org.skyscreamer.jsonassert.comparator.Customization; import net.minidev.json.JSONValue; ValueMatcher<Object> BOOL_INT_MATCHER = new ValueMatcher<Object>() { @Override public boolean equal(Object o1, Object o2) { // 处理布尔值与整数的双向匹配 if (o1 instanceof Boolean && o2 instanceof Integer) { Boolean boolVal = (Boolean) o1; Integer intVal = (Integer) o2; return (boolVal && intVal == 1) || (!boolVal && intVal == 0); } if (o1 instanceof Integer && o2 instanceof Boolean) { Integer intVal = (Integer) o1; Boolean boolVal = (Boolean) o2; return (intVal == 1 && boolVal) || (intVal == 0 && !boolVal); } // 其他类型用默认宽松比较 try { return JSONCompare.compareJSON( JSONValue.toJSONString(o1), JSONValue.toJSONString(o2), JSONCompareMode.LENIENT ).passed(); } catch (Exception e) { return o1.equals(o2); } } }; // 用通配符*将匹配器应用到所有字段 CustomComparator comparator = new CustomComparator( JSONCompareMode.LENIENT, new Customization("*", BOOL_INT_MATCHER) ); // 直接断言或手动获取差异结果 JSONAssert.assertEquals(expectedJsonAsString, actualJsonAsString, comparator); // 如需手动处理差异: // JSONCompareResult result = JSONCompare.compareJSON(expectedJsonAsString, actualJsonAsString, comparator); // if (!result.passed()) { // System.out.println("差异详情:" + result.getMessage()); // for (JSONComparisonFailure failure : result.getFailures()) { // System.out.printf("字段:%s,预期:%s,实际:%s%n", failure.getPath(), failure.getExpected(), failure.getActual()); // } // }
方案二:自定义CustomComparator子类
通过重写compareValues方法,直接在比较逻辑中处理类型匹配,更灵活可控,且无需依赖通配符配置:
import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.comparator.CustomComparator; import org.skyscreamer.jsonassert.JSONCompareResult; public class TypeAwareComparator extends CustomComparator { public TypeAwareComparator(JSONCompareMode mode) { super(mode); } @Override protected void compareValues(String path, Object expected, Object actual, JSONCompareResult result) throws Exception { boolean isMatched = false; // 处理布尔与整数的匹配逻辑 if (expected instanceof Boolean && actual instanceof Integer) { Boolean boolExp = (Boolean) expected; Integer intAct = (Integer) actual; isMatched = (boolExp && intAct == 1) || (!boolExp && intAct == 0); } else if (expected instanceof Integer && actual instanceof Boolean) { Integer intExp = (Integer) expected; Boolean boolAct = (Boolean) actual; isMatched = (intExp == 1 && boolAct) || (intExp == 0 && !boolAct); } if (isMatched) { return; // 匹配成功,不记录差异 } // 非目标类型对,使用默认比较逻辑 super.compareValues(path, expected, actual, result); } } // 使用自定义比较器 TypeAwareComparator comparator = new TypeAwareComparator(JSONCompareMode.LENIENT); JSONAssert.assertEquals(expectedJsonAsString, actualJsonAsString, comparator);
关键改进点
- 类型安全判断:直接通过
instanceof判断对象类型,避免字符串转换带来的误匹配问题 - 保留默认比较逻辑:仅处理目标类型对,其余场景沿用JSONAssert的宽松比较规则
- 正常输出差异:两种方案均能让JSONAssert自动生成详细的差异报告,包括不匹配的字段路径、预期值与实际值
内容的提问来源于stack exchange,提问作者Optimus Prime
相关产品推荐
相关产品推荐

