Skip to content

@JsonIdentityInfo with @JsonCreator incorrectly sets a field #3185

Description

@auke-

Using Jackson 2.12.3 I experience some unexpected behavior when using @JsonIdentityInfo in combination with an immutable class with a @JsonCreator annotated constructor. Jackson correctly uses the constructor in order to create the instance of the object, but then uses the field directly to set the value.

This poses a problem in GraalVM native images, where final fields aren't writeable by default. The behavior is also visible on a regular JVM and can be reproduced with the following code:

public class JacksonIssue {

  public static class PojoWithoutIdentityInfo {
    private final String fieldForId;

    @JsonCreator
    public PojoWithoutIdentityInfo(@JsonProperty("id") String fieldForId) {
      // Add a suffix to the given value in order to detect if the fields value has been altered
      this.fieldForId = fieldForId + "-from-constructor";
    }

    @JsonGetter("id")
    public String getFieldForId() {
      return fieldForId;
    }
  }

  @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
  public static class PojoWithIdentityInfo {
    private final String fieldForId;

    @JsonCreator
    public PojoWithIdentityInfo(@JsonProperty("id") String fieldForId) {
      // Add a suffix to the given value in order to detect if the fields value has been altered
      this.fieldForId = fieldForId + "-from-constructor";
    }

    @JsonGetter("id")
    public String getFieldForId() {
      return fieldForId;
    }
  }

  public static void main(String[] args) throws JsonProcessingException {
    String json = "{\"id\": \"valueFromJson\"}";

    PojoWithIdentityInfo pojoWithIdentityInfo = new ObjectMapper().readValue(json, PojoWithIdentityInfo.class);
    PojoWithoutIdentityInfo pojoWithoutIdentityInfo = new ObjectMapper().readValue(json, PojoWithoutIdentityInfo.class);

    System.out.println("pojo with identity info = " + pojoWithIdentityInfo.getFieldForId());
    System.out.println("pojo without identity info = " + pojoWithoutIdentityInfo.getFieldForId());
  }

Running this sample gives the following output:

pojo with identity info = valueFromJson
pojo without identity info = valueFromJson-from-constructor

It is clear that the value for the pojo with identity info is wrong and the field has been altered after it has been created using the constructor.

The issue seems originate from


or maybe from
return _fallbackSetter.setAndReturn(instance, value);
where the field is set.

Note that my real world issue isn't the changed value which was set in the constructor but the unnecessary reflective field access which poses a problem in GraalVM native images.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions