2

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<ShipmentData> shipments;

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

    public List<ShipmentData> 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?

3 Answers 3

1

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("shipments")
        @JsonAlias("listShipments")
                List<ShipmentData> 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("/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;
    }
}

Request:

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

Result:

{"shipments":["a","b","c"]}
Sign up to request clarification or add additional context in comments.

Comments

1

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:

<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>

Comments

0

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

1 Comment

Tried both. Exact same behavior

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.