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

Spring与Jackson版本迁移:Jackson 3无法反序列化父类final Map字段

问题

在企业项目从Spring 3.x迁移至Spring 4.x、Jackson 2.x迁移至Jackson 3.x的过程中,遇到Jackson 3无法识别content字段导致反序列化失败的问题——相同代码在Jackson 2中可正常运行。

示例代码(可在IDE JShell运行)

import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.json.JsonMapper;

import java.io.Serializable;
import java.util.*;

abstract class BaseMessage {
  protected UUID id;
  protected final Map<String, Serializable> content;
  // 其他字段…

  protected BaseMessage() {
    id = UUID.randomUUID();
    content = new HashMap<>();
  }

  public <T extends Serializable> void addContent(final String key, final T value) {
    content.put(key, value);
  }

  public <T extends Serializable> void addContent(final Map<String, T> contentToAdd) {
    content.putAll(contentToAdd);
  }

  public <T extends Serializable> void overrideContent(final Map<String, T> newContent) {
    content.clear();
    content.putAll(newContent);
  }

  public void setId(final UUID id) {
    this.id = id;
  }

  public UUID getId() {
    return id;
  }

  public Map<String, Serializable> getContent() {
    return content;
  }
}

class ClientMessage extends BaseMessage {
  private String upn;
  // 其他字段和方法…

  public void setUpn(final String upn) {
    this.upn = upn;
  }

  @Override
  public String toString() {
    return "ClientMessage{" + "upn='" + upn + "', id=" + id + ", content=" + content + '}';
  }
}

final String jsonMessage = """
  {
    "id": "00000000-0000-0000-0000-000000000000",
    "content": {
      "test": true,
      "prod": false,
      "demo": "Demonstration"
    },
    "upn": "nemo@exemple.org"
  }""";

// 主动启用FAIL_ON_UNKNOWN_PROPERTIES以突出Jackson 3+的问题
JsonMapper.builder()
    .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
    // 添加该配置仅为了在错误信息中包含源位置,无实际解决作用
    .enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION)
    .build()
    .readValue(jsonMessage, ClientMessage.class);

(注:原代码存在两处笔误:jdonMessage应为jsonMessage,enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION缺少闭合括号,已修正)

Jackson 3报错信息

Unrecognized property "content" (class ClientMessage), not marked as ignorable (2 known properties: "id", "upn")
at [Source: (String)"{
"id": "00000000-0000-0000-0000-000000000000",
"content": {
"test": true,
"prod": false,
"demo": "Demonstration"
},
"upn": "nemo@exemple.org"
}"; line: 3, column: 15] (through reference chain: ClientMessage["content"])

Jackson 2运行情况

Jackson 2中使用如下代码可正常反序列化:

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

// […]

new ObjectMapper()
          .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
          .readValue(jsonMessage, ClientMessage.class)

预期(及Jackson 2实际)结果:

[…] = ClientMessage{upn='nemo@exemple.org', id=00000000-0000-0000-0000-000000000000, content={test=true, prod=false, demo=Demonstration}}

解决方案

Jackson 3对JavaBean属性的识别规则做了严格调整,核心原因是:content字段是父类的final字段,且仅提供了getContent() getter方法,无对应的setContent() setter方法,Jackson 3默认不再将此类字段视为可反序列化的属性。

可通过以下几种方式修复:

1. 添加@JsonProperty注解标记content字段

直接在父类的content字段上添加tools.jackson.annotation.JsonProperty注解,明确告知Jackson该字段需要参与序列化/反序列化:

import tools.jackson.annotation.JsonProperty;

abstract class BaseMessage {
  protected UUID id;
  @JsonProperty
  protected final Map<String, Serializable> content;
  // 其他代码不变…
}

此方法最直接,无需修改现有方法结构。

2. 提供兼容的setContent方法

虽然content是final字段,但可以添加一个接收Map<String, Serializable>参数的setContent方法,内部调用overrideContent来更新内容:

abstract class BaseMessage {
  // 其他代码不变…
  public void setContent(Map<String, Serializable> newContent) {
    overrideContent(newContent);
  }
}

Jackson 3会识别到setContent方法,将JSON中的content字段映射到该方法完成反序列化。

3. 配置Jackson启用兼容模式(不推荐)

如果不想修改实体类,可以通过配置Jackson的反序列化特性,允许通过getter方法对应的字段进行反序列化,但此方法可能引入其他兼容性问题,仅作为临时方案:

JsonMapper.builder()
    .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
    .enable(DeserializationFeature.USE_GETTERS_AS_SETTERS) // 启用getter作为setter的兼容模式
    .build()
    .readValue(jsonMessage, ClientMessage.class);

注意:USE_GETTERS_AS_SETTERS是Jackson 3中新增的兼容特性,用于适配旧版本的行为,但长期来看建议优先使用前两种方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 15:24:51