Jackson 在构造函数中使用 @JsonProperty 名称进行序列化

Jackson serializes using @JsonProperty name in constructor

我一直在努力升级我的一个微服务的 Spring 启动版本,我偶然发现了一个奇怪的行为。我有 class 这样的:

public class FilteredData {

    private final List<ShipmentData> shipments;

    public FilteredData(@JsonProperty("listShipments") List<ShipmentData> shipments) {
        this.shipments = shipments;
    }

    public List<ShipmentData> getShipments() {
        return shipments;
    }
}

我在升级之前的行为是,在反序列化时,名称 listShipments 在 JSON 对象中用于映射到 shipments 属性 的 Java class。但是,在序列化时,它会使用名称 shipments 而不是 listShipments 写入 shipments 属性。 问题是现在它在反序列化和序列化时都使用名称 listShipments 。我不确定这个问题是从什么时候开始发生的,因为我最初的 Spring 引导版本是 1.5.7,我正在慢慢升级到 2.3.4。但我相信它是在 2.0.0 版本之后开始发生的。

我不知道这是由于 Spring Boot 的 Jackson 自动配置中的某些内部更改引起的,还是实际 Jackson 库中的更改引起的,但我很难追踪导致此问题的原因和如何修复它。

编辑:我注意到从最新的 Spring Boot 1 版本 (1.5.22) 到 Spring Boot 2.0.0,Jackson 次要版本被提升了(从 2.8 到 2.9)。这会导致问题吗?

Spring Boot 1 和 Spring Boot 2 之间发生了重大变化。您是否已尝试修复它?例如。试图将 JsonProperty 注释移动到字段而不是构造函数?或者使用@JsonCreator

在线资源:https://www.baeldung.com/jackson-deserialize-immutable-objects

从 Spring Boot 1.5.7.RELEASE 切换到 2.0.[=53 时,我能够重现这个确切的行为=]。首先,我添加了父项和 spring-boot-starter-web 依赖项。然后,我创建了一个简单的 @RestController,使用您提供的 POJO 并将 ShipmentData 替换为 String。当我手动创建 Jackson ObjectMapper 时,我无法在两个版本中重现该问题。

最有趣的部分:当您将构造函数的参数名称重命名为 shipments 以外的名称时,它似乎工作正常。这可能是 Jackson 或 Spring 框架中某处的错误,只有当 getter 与构造函数中的参数名称匹配时才会出现。

我建议使用@JsonAlias来定义在反序列化过程中应该接受的替代名称:

@JsonCreator
public FilteredData(
        @JsonProperty("shipments")
        @JsonAlias("listShipments")
                List<ShipmentData> shipments) {
    this.shipments = shipments;
}

这样,您可以同时使用 listShipmentsshipments 进行反序列化,而对于序列化,仅使用 shipments(由 getter 定义)。


供参考,使用 2.3.3.RELEASE:

的工作实现
@RestController
public class FilteredDataController {

    @PostMapping("/data")
    public FilteredData postFilteredData(@RequestBody FilteredData data) {
        return data;
    }
}
public class FilteredData {
    
    private final List<String> shipments;

    @JsonCreator
    public FilteredData(
            @JsonProperty("shipments") @JsonAlias("listShipments") 
                    List<String> shipments) {
        this.shipments = shipments;
    }

    public List<String> getShipments() {
        return shipments;
    }
}

要求:

curl -d '{"listShipments":["a","b","c"]}' -H 'Content-Type: application/json' http://localhost:8080/data

结果:

{"shipments":["a","b","c"]}

我找到问题了!这是由 spring-boot-starter-web 使用名为 jackson-module-parameter-names 的依赖项引起的。这个库提供了一个名为 ParameterNamesModule 的 Jackson 模块,根据 Spring Boot 的 Jackson 自动配置,在类路径中找到的任何 Jackson 模块都会自动导入。

https://github.com/FasterXML/jackson-modules-java8 中对该库的描述说:

Parameter names: support for detecting constructor and factory method ("creator") parameters without having to use @JsonProperty annotation. Provides com.fasterxml.jackson.module.paramnames.ParameterNamesModule

我仍然不是 100% 确定为什么这会造成它所造成的问题,但通过 Maven 删除它解决了问题:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-module-parameter-names</artifactId>
        </exclusion>
    </exclusions>
</dependency>