0

0

深入理解Jackson @JsonMerge 实现深度合并与防止数据覆盖

碧海醫心

碧海醫心

发布时间:2025-11-18 18:08:01

|

550人浏览过

|

来源于php中文网

原创

深入理解Jackson @JsonMerge 实现深度合并与防止数据覆盖

在使用jackson进行对象更新时,默认行为可能导致json请求中缺失的字段覆盖现有数据为`null`。本文将深入探讨这一常见问题,并详细介绍如何利用jackson 2.9及更高版本引入的`@jsonmerge`注解,实现智能的深度合并(deep merging),从而有效防止在部分更新操作中意外丢失现有数据,确保数据完整性。

Jackson ObjectReader 更新操作中的数据覆盖问题

在许多应用场景中,我们经常需要对数据库中已有的数据进行部分更新。当使用Jackson库将传入的JSON请求合并到现有Java对象时,一个常见的痛点是:如果JSON请求中某个字段缺失,Jackson的默认行为会将Java对象中对应的非空字段覆盖为null,而不是保留其原有值。这对于需要进行局部更新(partial update)的API来说,是一个不期望的行为。

考虑以下数据模型:

data class Model(
  val fieldTypeA: FieldTypeA? = null,
)

data class FieldTypeA(
  val valueA: String? = null,
  val valueB: String? = null,
)

假设我们有一个Model实例,其fieldTypeA.valueA已赋值:

// 现有数据,从数据库读取
val existingModel = Model(fieldTypeA = FieldTypeA(valueA = "Test", valueB = "ExistingB"))
// 此时 existingModel.fieldTypeA.valueA = "Test"

现在,我们收到一个JSON更新请求,该请求只包含fieldTypeA中的valueB字段:

{
  "fieldTypeA": {
    "valueB": "I am value B"
  }
}

当我们尝试使用ObjectReader将此JSON合并到existingModel时:

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.ObjectReader

// 假设 CustomMapper 是一个配置好的ObjectMapper实例
val mapper = ObjectMapper()
val readerForUpdating: ObjectReader = mapper.readerForUpdating(existingModel)

val jsonRequestString = """{"fieldTypeA":{"valueB":"I am value B"}}"""
val updatedModel: Model = readerForUpdating.readValue(jsonRequestString)

在上述操作后,updatedModel的结果将是:

// 预期结果:Model(fieldTypeA = FieldTypeA(valueA = "Test", valueB = "I am value B"))
// 实际结果:Model(fieldTypeA = FieldTypeA(valueA = null, valueB = "I am value B"))

可以看到,fieldTypeA.valueA被意外地覆盖为null,因为在jsonRequest中它没有被明确提供。这种行为在处理复杂嵌套对象的部分更新时尤其 problematic。

KAIZAN.ai
KAIZAN.ai

使用AI来改善客户服体验,提高忠诚度

下载

解决方案:使用 @JsonMerge 注解实现深度合并

为了解决上述数据覆盖问题,Jackson从2.9版本开始引入了@JsonMerge注解,它允许开发者指定在合并过程中,对于某个复杂的嵌套对象字段,即使传入的JSON中没有该字段的完整信息,也应尝试进行深度合并,而不是简单地替换整个对象。

如何应用 @JsonMerge

要启用深度合并行为,只需将@JsonMerge注解应用于Java(或Kotlin)数据模型中需要进行深度合并的字段上。在我们的示例中,Model类中的fieldTypeA字段是一个嵌套对象,因此我们应将其标记为@JsonMerge:

import com.fasterxml.jackson.annotation.JsonMerge

data class Model(
  @JsonMerge // 在这里添加 @JsonMerge 注解
  val fieldTypeA: FieldTypeA? = null,
)

data class FieldTypeA(
  val valueA: String? = null,
  val valueB: String? = null,
)

通过添加@JsonMerge注解,当Jackson处理fieldTypeA字段时,它将不再简单地用JSON中提供的新对象替换整个fieldTypeA,而是会尝试将JSON中的值与现有fieldTypeA对象进行合并。这意味着:

  • 如果JSON中提供了fieldTypeA的某个子字段(如valueB),则该子字段会被更新。
  • 如果JSON中未提供fieldTypeA的某个子字段(如valueA),则该子字段在现有对象中的值将被保留,而不会被null覆盖。

验证合并行为

使用带有@JsonMerge注解的Model类再次执行更新操作:

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.ObjectReader
import com.fasterxml.jackson.annotation.JsonMerge // 确保导入

// ... (Model 和 FieldTypeA 定义,Model.fieldTypeA 已添加 @JsonMerge)

val mapper = ObjectMapper()

// 现有数据
val existingModel = Model(fieldTypeA = FieldTypeA(valueA = "Test", valueB = "ExistingB"))

// 创建 ObjectReader,并传入 existingModel 作为更新目标
val readerForUpdating: ObjectReader = mapper.readerForUpdating(existingModel)

// JSON 更新请求
val jsonRequestString = """{"fieldTypeA":{"valueB":"I am value B"}}"""
val updatedModel: Model = readerForUpdating.readValue(jsonRequestString)

// 此时 updatedModel 的结果将是:
// Model(fieldTypeA = FieldTypeA(valueA = "Test", valueB = "I am value B"))

现在,updatedModel.fieldTypeA.valueA的值成功保留了"Test",而valueB则被更新为"I am value B"。这正是我们期望的深度合并行为。

注意事项与总结

  1. Jackson 版本要求: @JsonMerge注解是在Jackson 2.9版本中引入的。如果您的项目使用的是旧版Jackson,则需要升级到2.9或更高版本才能使用此功能。
  2. 适用场景: @JsonMerge特别适用于处理RESTful API中的PATCH操作,即只更新资源的一部分字段。它确保了在进行局部更新时,未在请求中明确指定的复杂对象字段不会被意外地清空。
  3. 注解位置: @JsonMerge应应用于需要进行深度合并的复杂对象字段上,而不是基本类型字段。
  4. 默认行为: 对于未标记@JsonMerge的字段,Jackson的默认行为仍然是替换(如果JSON中存在)或覆盖为null(如果JSON中缺失但Java对象中有值)。
  5. 性能考量: 深度合并操作可能比简单的对象替换消耗更多的CPU资源,因为它需要遍历并比较字段。但在大多数业务场景中,这种开销通常是可接受的,并且其带来的数据完整性优势远超性能影响。

通过合理利用@JsonMerge注解,开发者可以显著提升Jackson在处理部分更新时的健壮性和灵活性,避免不必要的数据丢失,从而构建更加可靠和用户友好的应用程序。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

832

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

738

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

734

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16926

2023.08.03

Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

8

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.8万人学习

Java 教程
Java 教程

共578课时 | 46.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号