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

如何使用Jackson为两个对象创建JSON Merge Patch文档

问题背景

我们的应用重度依赖Jackson处理JSON,REST API里有大量带注解的模型对象。现在需要调用一个要求提交Merge Patch(符合RFC7396规范)JSON文档的REST API。目前用JSON-P能轻松实现,但没找到Jackson原生的实现方式。

现有实现代码如下:

public <T> String createMergePatch(T source, T target) throws IOException {
    var sourceBytes = objectMapper.writeValueAsBytes(source);
    var targetBytes = objectMapper.writeValueAsBytes(target);

    // 切换框架——非常不理想
    var sourceJson = Json.createReader(new ByteArrayInputStream(sourceBytes)).readValue();
    var targetJson = Json.createReader(new ByteArrayInputStream(targetBytes)).readValue();
    var mergePatch = Json.createMergeDiff(sourceJson, targetJson);
    return mergePatch.toJsonValue().toString();
}

这段代码先用Jackson注解把源对象和目标对象转成JSON,然后切换到其他框架调用createMergeDiff方法。虽然输出结果正确,但这种方式效率低,还引入了不必要的依赖。

核心问题

有没有更优的Jackson原生实现方式?不需要引入其他JSON框架,也不用在JSON和对象之间做往返转换?


解决方案

可以直接利用Jackson的树模型(JsonNode)和官方扩展库,或者手动实现对比逻辑,完全基于Jackson原生能力解决问题,避免额外依赖和不必要的转换开销。

方法一:使用Jackson官方扩展库(推荐)

Jackson官方维护的jackson-datatype-json-patch扩展库原生支持RFC7396的Merge Patch规范,完全兼容现有带注解的模型对象。

步骤1:引入依赖(Maven示例)

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-json-patch</artifactId>
    <version>匹配你的Jackson核心版本</version>
</dependency>

步骤2:实现Merge Patch生成

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsonpatch.MergePatch;
import java.io.IOException;

public <T> String createMergePatch(T source, T target) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    // 直接将对象转为JsonNode,跳过字节流序列化/解析的往返步骤
    JsonNode sourceNode = mapper.valueToTree(source);
    JsonNode targetNode = mapper.valueToTree(target);
    
    // 生成符合规范的Merge Patch
    MergePatch mergePatch = MergePatch.fromDiff(sourceNode, targetNode);
    
    // 将Patch转为JSON字符串
    return mapper.writeValueAsString(mergePatch);
}

方法二:手动实现Merge Diff逻辑(无额外依赖)

如果不想引入扩展库,可以基于Jackson的JsonNode手动实现RFC7396的对比规则:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;

public <T> String createMergePatch(T source, T target) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    JsonNode sourceNode = mapper.valueToTree(source);
    JsonNode targetNode = mapper.valueToTree(target);
    
    ObjectNode patchNode = mapper.createObjectNode();
    generateMergePatch(sourceNode, targetNode, patchNode);
    
    return mapper.writeValueAsString(patchNode);
}

private void generateMergePatch(JsonNode source, JsonNode target, ObjectNode patch) {
    if (source.equals(target)) {
        return;
    }
    
    // RFC7396规定数组直接整体替换
    if (source.isArray() && target.isArray()) {
        patch.set("", target);
        return;
    }
    
    // 处理对象类型的字段差异
    if (source.isObject() && target.isObject()) {
        // 新增/修改的字段
        target.fieldNames().forEachRemaining(fieldName -> {
            JsonNode sourceField = source.get(fieldName);
            JsonNode targetField = target.get(fieldName);
            
            if (sourceField == null) {
                patch.set(fieldName, targetField);
            } else {
                generateMergePatch(sourceField, targetField, patch.withObject(fieldName));
                // 递归后无差异则移除空节点
                if (patch.get(fieldName).isEmpty()) {
                    patch.remove(fieldName);
                }
            }
        });
        
        // 处理字段删除:源存在但目标不存在的字段,Patch中设为null
        source.fieldNames().forEachRemaining(fieldName -> {
            if (target.get(fieldName) == null) {
                patch.putNull(fieldName);
            }
        });
    } else {
        // 非对象/数组类型,直接替换
        patch.set("", target);
    }
}

方案优势

  • 完全基于Jackson原生能力,无额外JSON框架依赖
  • 直接将对象转为JsonNode,避免序列化字节流再解析的往返开销,效率更高
  • 完全兼容原有模型对象的Jackson注解,不破坏现有业务逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 20:55:20