You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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);

关键改进点

  1. 类型安全判断:直接通过instanceof判断对象类型,避免字符串转换带来的误匹配问题
  2. 保留默认比较逻辑:仅处理目标类型对,其余场景沿用JSONAssert的宽松比较规则
  3. 正常输出差异:两种方案均能让JSONAssert自动生成详细的差异报告,包括不匹配的字段路径、预期值与实际值

内容的提问来源于stack exchange,提问作者Optimus Prime

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 19:15:31