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

huangapple go评论78阅读模式
英文:

Jackson serializes using @JsonProperty name in constructor

问题

我一直在升级我的一个微服务的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;
    }
}

在升级之前,我遇到的行为是,在反序列化时,JSON对象中的名称listShipments被用来映射到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)。这可能导致了这个问题吗?

英文:

I have been working on upgrading the Spring Boot version of one of my microservices, and I stumbled upon a strange behaviour. I have class like this:

public class FilteredData {

    private final List&lt;ShipmentData&gt; shipments;

    public FilteredData(@JsonProperty(&quot;listShipments&quot;) List&lt;ShipmentData&gt; shipments) {
        this.shipments = shipments;
    }

    public List&lt;ShipmentData&gt; getShipments() {
        return shipments;
    }
}

The behaviour that I had before doing the upgrade was that, when deserialising, the name listShipments was used in the JSON object to map to the shipments property of the Java class. However, when serialising, it would write the shipments property with the name shipments, not listShipments.
The problem is that now it is using the name listShipments when both deserialising and serialising. I am not sure at what point this issue started happening, as my initial Spring Boot version was 1.5.7 and I am slowly upgrading all the way to 2.3.4. But I believe it started happening after version 2.0.0.

I don't know if this is being caused by some internal change in Spring Boot's Jackson autoconfiguration, or a change in the actual Jackson library, but I am having a hard time tracking what caused this and how to fix it.

EDIT: I noticed from the latest Spring Boot 1 version (1.5.22) to Spring Boot 2.0.0, the Jackson minor version was bumped (from 2.8 to 2.9). Could this have caused the issue?

答案1

得分: 1

以下是翻译好的内容:

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

最有趣的部分是:当你将构造函数的参数名称重命名为与 shipments 不同的其他名称时,似乎就能正常工作。这可能是Jackson中的一个bug,或者是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"]}
英文:

I was able to reproduce this exact behaviour while switching from Spring Boot 1.5.7.RELEASE to 2.0.0.RELEASE. First, I added the parent and the spring-boot-starter-web dependency. Then, I created a simple @RestController, used the POJO you provided and replaced ShipmentData with String. When I manually create a Jackson ObjectMapper, I could not reproduce the issue across the two releases.

The most interesting part: When you rename the parameter name of the constructor to something other than shipments, it seems to work fine. This could possibly be a bug in Jackson or somewhere in the Spring Framework which manifests only when the getter matches the parameter name in the constructor.

I suggest to use @JsonAlias to define the alternative name that should be accepted during deserialization:

@JsonCreator
public FilteredData(
        @JsonProperty(&quot;shipments&quot;)
        @JsonAlias(&quot;listShipments&quot;)
                List&lt;ShipmentData&gt; shipments) {
    this.shipments = shipments;
}

With this, you can deserialize with both listShipments and shipments while for serialization only shipments is used (defined by getter).


For reference, a working implementation with 2.3.3.RELEASE:

@RestController
public class FilteredDataController {

    @PostMapping(&quot;/data&quot;)
    public FilteredData postFilteredData(@RequestBody FilteredData data) {
        return data;
    }
}
public class FilteredData {
    
    private final List&lt;String&gt; shipments;

    @JsonCreator
    public FilteredData(
            @JsonProperty(&quot;shipments&quot;) @JsonAlias(&quot;listShipments&quot;) 
                    List&lt;String&gt; shipments) {
        this.shipments = shipments;
    }

    public List&lt;String&gt; getShipments() {
        return shipments;
    }
}

Request:

curl -d &#39;{&quot;listShipments&quot;:[&quot;a&quot;,&quot;b&quot;,&quot;c&quot;]}&#39; -H &#39;Content-Type: application/json&#39; http://localhost:8080/data

Result:

{&quot;shipments&quot;:[&quot;a&quot;,&quot;b&quot;,&quot;c&quot;]}

答案2

得分: 1

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

在https://github.com/FasterXML/jackson-modules-java8中,对该库的描述如下:

参数名:支持检测构造函数和工厂方法("creator")的参数,无需使用@JsonProperty注解。提供com.fasterxml.jackson.module.paramnames.ParameterNamesModule

我仍然不确定为什么这会引发问题,但是通过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>
英文:

I managed to find the issue! It was caused by a dependency being used by spring-boot-starter-web called jackson-module-parameter-names. This library provides a Jackson module named ParameterNamesModule, which, as per Spring Boot's Jackson Autoconfiguration, any Jackson module found in the classpath is automatically imported.

The description for this library in https://github.com/FasterXML/jackson-modules-java8 says:

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

I am not 100% sure still why this created the problem that it did, but removing it through Maven solved the issue:

&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
    &lt;exclusions&gt;
        &lt;exclusion&gt;
            &lt;groupId&gt;com.fasterxml.jackson.module&lt;/groupId&gt;
            &lt;artifactId&gt;jackson-module-parameter-names&lt;/artifactId&gt;
        &lt;/exclusion&gt;
    &lt;/exclusions&gt;
&lt;/dependency&gt;

答案3

得分: 0

以下是翻译好的内容:

在Spring Boot 1和Spring Boot 2之间存在重大变化。你已经尝试过解决这个问题了吗?例如,尝试将JsonProperty注解从构造函数移到字段中,而不是使用@JsonCreator。

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

英文:

There were breaking changes between Spring Boot 1 and Spring Boot 2. Have you tried anything to fix it already? E.g. attempted to move the JsonProperty annotation to field rather than constructor? or using @JsonCreator

online resource: https://www.baeldung.com/jackson-deserialize-immutable-objects

huangapple
  • 本文由 发表于 2020年10月6日 22:13:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/64227635.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定