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

Java Jackson实现两JSON对比 填充首个对象null字段为第二对象非空值

可行性说明

该需求可直接通过Jackson原生JsonNode能力实现,无需引入额外依赖。核心规则为:仅对第一个JSON(目标对象)中缺失、或值为null的字段,用第二个JSON(源对象)的同名字段非空值填充,全程不覆盖目标对象已有的非空字段值。

实现逻辑

核心采用递归遍历处理,规则如下:

  • 遍历源对象的所有字段,跳过源对象中值为null的字段
  • 对每个源字段,检查目标对象的同名字段:
    • 目标无该字段、或字段值为null:将源字段的深拷贝值写入目标
    • 目标同名字段为非空对象、源字段也为对象类型:递归进入两个对象做深层字段合并
    • 目标同名字段为非空的基本类型、数组类型:直接保留目标原有值,不做任何覆盖
代码实现

首先引入Jackson核心依赖(如果你项目中已经引入jackson-databind可跳过),然后编写合并工具类:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.Iterator;
import java.util.Map;

public class JsonMergeUtils {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    /**
     * 非覆盖式JSON合并:仅填充目标对象缺失/值为null的字段,不覆盖已有非空值
     * @param target 待补全的目标JSON对象(即第一个JSON)
     * @param source 提供补全字段的源JSON对象(即第二个JSON)
     * @return 合并完成的目标JSON对象
     */
    public static ObjectNode mergeNullAbsentFields(ObjectNode target, ObjectNode source) {
        Iterator<Map.Entry<String, JsonNode>> sourceFields = source.fields();
        while (sourceFields.hasNext()) {
            Map.Entry<String, JsonNode> entry = sourceFields.next();
            String fieldName = entry.getKey();
            JsonNode sourceValue = entry.getValue();

            // 源字段本身为null,无填充价值直接跳过
            if (sourceValue.isNull()) {
                continue;
            }

            JsonNode targetValue = target.get(fieldName);
            if (targetValue == null || targetValue.isNull()) {
                // 目标字段缺失或为null,写入源字段的深拷贝避免引用关联
                target.set(fieldName, sourceValue.deepCopy());
            } else if (targetValue.isObject() && sourceValue.isObject()) {
                // 双方都是非空对象,递归做深层合并
                mergeNullAbsentFields((ObjectNode) targetValue, (ObjectNode) sourceValue);
            }
            // 其余场景(目标为非空数组、基本类型):保留原值不处理
        }
        return target;
    }

    // 测试方法
    public static void main(String[] args) throws Exception {
        // 测试用例1:第一个JSON为缺失additionalInfo的形式一
        String json1Form1 = "{\n" +
                "  \"name\": \"test_name\",\n" +
                "  \"contact\": [\n" +
                "     {\"street\": \"st.1\", \"phoneNumber\": \"test_n\"}\n" +
                "   ]\n" +
                "}";
        // 测试用例2:第一个JSON为additionalInfo为null的形式二
        String json1Form2 = "{\n" +
                "  \"name\": \"test_name\",\n" +
                "  \"contact\": [\n" +
                "     {\"street\": \"st.1\", \"phoneNumber\": \"test_n\"}\n" +
                "   ],\n" +
                "  \"additionalInfo\": null\n" +
                "}";
        String json2 = "{\n" +
                "  \"name\": \"test_name_2\",\n" +
                "  \"additionalInfo\": {\"lastName\": \"ln\", \"age\": 24}\n" +
                "}";

        ObjectNode target = (ObjectNode) OBJECT_MAPPER.readTree(json1Form1);
        ObjectNode source = (ObjectNode) OBJECT_MAPPER.readTree(json2);
        ObjectNode result = mergeNullAbsentFields(target, source);
        
        // 打印格式化结果
        System.out.println(OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(result));
    }
}
运行结果

两种形式的第一个JSON执行后,输出均完全符合预期:

{
  "name" : "test_name",
  "contact" : [ {
    "street" : "st.1",
    "phoneNumber" : "test_n"
  } ],
  "additionalInfo" : {
    "lastName" : "ln",
    "age" : 24
  }
}
扩展说明
  • 上述实现默认不处理数组内部元素的合并,如果需要对数组内对象按相同规则补全字段,可以在判断分支中增加数组节点的遍历合并逻辑
  • 写入值时调用deepCopy()是为了切断源和目标的节点引用关联,避免后续修改任意一方节点时产生意外联动
  • 如果需要忽略字段大小写、自定义特殊字段合并规则,可以在遍历字段时增加对应的判断逻辑即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 18:18:38