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

JUnit中比较JSON对象:需无视键值顺序并忽略指定动态字段

解决JUnit中JSON对象比较的问题:忽略动态字段与键值对顺序

我之前也遇到过一模一样的问题!JUnit默认的assertEquals比较的是对象引用,而不是实际的字段内容,所以哪怕两个对象的字段值完全一致,只要是不同的实例就会抛出java.lang.AssertionError。针对你需要忽略动态字段(比如timestamp、id)、不关心JSON键值对顺序的需求,这里有几个靠谱的解决方案:

方案一:利用Gson的JsonElement进行内容比较(无需额外依赖)

Gson的JsonElement类重写了equals方法,会递归比较JSON的内容,而且不关心键的顺序。我们可以通过序列化时排除指定字段,只保留需要校验的部分来实现需求。

步骤1:配置Gson排除动态字段

有两种方式排除字段:

  • 方式A:使用@Expose注解:在实体类的需要校验的字段上添加@Expose,动态字段不加,然后构建Gson时开启排除未标注的字段。
  • 方式B:自定义排除策略:直接指定要忽略的字段名,无需修改实体类。

示例代码(方式A):

import com.google.gson.annotations.Expose;

public class Data {
    @Expose // 需要校验的字段
    private String name;
    @Expose
    private String age;
    private String timestamp; // 忽略的动态字段
    private String id; // 忽略的动态字段

    // 生成getter、setter方法
}

测试代码:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class DataTest {
    @Test
    public void testJsonEqualityIgnoringDynamicFields() {
        String json1 = "{\"name\": \"ray\", \"age\": \"20\", \"timestamp\": \"03:10:54\", \"id\": \"123\"}";
        String json2 = "{\"age\": \"20\", \"name\": \"ray\", \"timestamp\": \"04:15:22\", \"id\": \"456\"}";

        // 构建Gson实例,排除未标注@Expose的字段
        Gson gson = new GsonBuilder()
                .excludeFieldsWithoutExposeAnnotation()
                .create();

        Data data1 = gson.fromJson(json1, Data.class);
        Data data2 = gson.fromJson(json2, Data.class);

        // 把对象转回JsonElement,比较内容
        JsonElement jsonElement1 = gson.toJsonTree(data1);
        JsonElement jsonElement2 = gson.toJsonTree(data2);

        assertEquals(jsonElement1, jsonElement2); // 断言成功,因为忽略了动态字段且不关心键顺序
    }
}

示例代码(方式B,无需修改实体类):

如果不想修改实体类,可以用addSerializationExclusionStrategy指定要排除的字段:

Gson gson = new GsonBuilder()
        .addSerializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                // 返回true表示排除该字段
                return "timestamp".equals(f.getName()) || "id".equals(f.getName());
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return false;
            }
        })
        .create();

方案二:使用AssertJ库(更灵活简洁)

AssertJ是一个增强型的断言库,提供了强大的递归对象比较功能,可以直接指定忽略的字段,写法非常直观,无需修改实体类或Gson配置。

测试代码示例:

import com.google.gson.Gson;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;

public class DataTest {
    @Test
    public void testDataEqualityWithAssertJ() {
        String json1 = "{\"name\": \"ray\", \"age\": \"20\", \"timestamp\": \"03:10:54\", \"id\": \"123\"}";
        String json2 = "{\"age\": \"20\", \"name\": \"ray\", \"timestamp\": \"04:15:22\", \"id\": \"456\"}";

        Gson gson = new Gson();
        Data data1 = gson.fromJson(json1, Data.class);
        Data data2 = gson.fromJson(json2, Data.class);

        // 递归比较对象,忽略指定字段
        assertThat(data1)
                .usingRecursiveComparison()
                .ignoringFields("timestamp", "id")
                .isEqualTo(data2); // 断言成功
    }
}

方案三:手动比较字段(适合字段较少的场景)

如果你的实体类字段不多,也可以手动逐个比较需要校验的字段,跳过动态字段:

@Test
public void testDataEqualityManually() {
    String json1 = "{\"name\": \"ray\", \"age\": \"20\", \"timestamp\": \"03:10:54\", \"id\": \"123\"}";
    String json2 = "{\"age\": \"20\", \"name\": \"ray\", \"timestamp\": \"04:15:22\", \"id\": \"456\"}";

    Gson gson = new Gson();
    Data data1 = gson.fromJson(json1, Data.class);
    Data data2 = gson.fromJson(json2, Data.class);

    assertEquals(data1.getName(), data2.getName());
    assertEquals(data1.getAge(), data2.getAge());
    // 不比较timestamp和id字段
}

总结

  • 如果你不想引入额外依赖,优先选方案一;
  • 如果追求代码简洁灵活,推荐使用方案二(AssertJ在Java测试中非常常用,功能远超JUnit默认断言);
  • 字段极少的情况下可以用方案三。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 09:40:32