【问题标题】:Jackson serializes using @JsonProperty name in constructorJackson 在构造函数中使用 @JsonProperty 名称进行序列化
【发布时间】:2020-10-06 14:13:33
【问题描述】:

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

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 对象中用于映射到 Java 类的 shipments 属性。但是,在序列化时,它会将shipments 属性写入名称为shipments,而不是listShipments。 问题是现在它在反序列化和序列化时都使用名称listShipments。我不确定这个问题是从什么时候开始发生的,因为我最初的 Spring Boot 版本是 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)。这会导致问题吗?

【问题讨论】:

    标签: java spring spring-boot jackson


    【解决方案1】:

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

    最有趣的部分:当您将构造函数的参数名称重命名为shipments 以外的名称时,它似乎工作正常。这可能是 Jackson 或 Spring Framework 中的某个错误,只有当 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"]}
    

    【讨论】:

      【解决方案2】:

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

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

      参数名称:支持检测构造函数和工厂方法(“创建者”)参数,而无需使用@JsonProperty 注解。提供 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>
      

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

        • 都试过了。完全相同的行为
        猜你喜欢
        • 2014-05-24
        • 2016-10-18
        • 1970-01-01
        • 2020-10-20
        • 2021-01-12
        • 2016-02-11
        • 2020-03-26
        • 2013-07-11
        相关资源
        最近更新 更多