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
|
return idProp.setAndReturn(bean, _idValue); |
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.
Using Jackson
2.12.3I 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:
Running this sample gives the following output:
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
jackson-databind/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java
Line 256 in cc1a04b
or maybe from
jackson-databind/src/main/java/com/fasterxml/jackson/databind/deser/CreatorProperty.java
Line 293 in cc1a04b
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.