`@JacksonInject` added to property overrides value from the JSON even if `useInput` is `OptBoolean.TRUE`
See original GitHub issueHello,
I have problem when using @JacksonInject
on final
field that is initialized using constructor (see the whole example at the end of the message).
The problem is that the correct value from JSON is used when the constructor is called, see the stacktrace:
AnnotatedConstructor.call(Object[]) line: 124
StdValueInstantiator.createFromObjectWith(DeserializationContext, Object[]) line: 283
StdValueInstantiator(ValueInstantiator).createFromObjectWith(DeserializationContext, SettableBeanProperty[], PropertyValueBuffer) line: 229
PropertyBasedCreator.build(DeserializationContext, PropertyValueBuffer) line: 195
BeanDeserializer._deserializeUsingPropertyBased(JsonParser, DeserializationContext) line: 422
BeanDeserializer(BeanDeserializerBase).deserializeFromObjectUsingNonDefault(JsonParser, DeserializationContext) line: 1287
BeanDeserializer.deserializeFromObject(JsonParser, DeserializationContext) line: 326
BeanDeserializer.deserialize(JsonParser, DeserializationContext) line: 159
ObjectMapper._readMapAndClose(JsonParser, JavaType) line: 4013
ObjectMapper.readValue(String, Class<T>) line: 3004
JacksonInjectTest.testReadValueInjectables()
But later this value is overrided by the default value of the injectable (but useInput
is OptBoolean.TRUE
) because _injectables != null
at BeanDeserializer.deserialize(JsonParser, DeserializationContext, Object) line: 216
, full stacktrace:
AnnotatedField.setValue(Object, Object) line: 105
ValueInjector.inject(DeserializationContext, Object) line: 51
BeanDeserializer(BeanDeserializerBase).injectValues(DeserializationContext, Object) line: 1520
BeanDeserializer.deserialize(JsonParser, DeserializationContext, Object) line: 217
BeanDeserializer._deserializeUsingPropertyBased(JsonParser, DeserializationContext) line: 441
BeanDeserializer(BeanDeserializerBase).deserializeFromObjectUsingNonDefault(JsonParser, DeserializationContext) line: 1287
BeanDeserializer.deserializeFromObject(JsonParser, DeserializationContext) line: 326
BeanDeserializer.deserialize(JsonParser, DeserializationContext) line: 159
ObjectMapper._readMapAndClose(JsonParser, JavaType) line: 4013
ObjectMapper.readValue(String, Class<T>) line: 3004
JacksonInjectTest.testReadValueInjectables()
This happens even if I set mapper.configure(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS, false);
which is weired.
Here is the whole example:
package com.drooms.semantic.flink.jobs;
import static java.util.Objects.requireNonNull;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import org.testng.annotations.Test;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.OptBoolean;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonInjectTest {
protected static class Some {
private final String field1;
@JacksonInject(value = "defaultValueForField2", useInput = OptBoolean.TRUE)
private final String field2;
public Some(@JsonProperty("field1") final String field1,
@JsonProperty("field2") @JacksonInject(value = "defaultValueForField2",
useInput = OptBoolean.TRUE) final String field2) {
this.field1 = requireNonNull(field1);
this.field2 = requireNonNull(field2);
}
public String getField1() {
return field1;
}
public String getField2() {
return field2;
}
}
@Test
public void testReadValueInjectables() throws JsonParseException, JsonMappingException, IOException {
final ObjectMapper mapper = new ObjectMapper();
final InjectableValues injectableValues =
new InjectableValues.Std().addValue("defaultValueForField2", "somedefaultValue");
mapper.setInjectableValues(injectableValues);
final Some actualValueMissing = mapper.readValue("{\"field1\": \"field1value\"}", Some.class);
assertEquals(actualValueMissing.getField1(), "field1value");
assertEquals(actualValueMissing.getField2(), "somedefaultValue");
final Some actualValuePresent =
mapper.readValue("{\"field1\": \"field1value\", \"field2\": \"field2value\"}", Some.class);
assertEquals(actualValuePresent.getField1(), "field1value");
assertEquals(actualValuePresent.getField2(), "somedefaultValue");
// if I comment @JacksonInject that is next to the property the valid assert is the correct one:
// assertEquals(actualValuePresent.getField2(), "field2value");
}
}
In my original case I’m using lombok to generate the constructor and the annotation is copied from property to the constructor parameter (you can check it here: https://github.com/rzwitserloot/lombok/issues/1528#issuecomment-607725333 ). You can check https://stackoverflow.com/a/60987798/2054634 for more information on the context of the problem.
P.S. I’m using jackson v. 2.9.6
Thanks in advance, Plamen
Issue Analytics
- State:
- Created 3 years ago
- Comments:9 (5 by maintainers)
Top GitHub Comments
Thank you for reporting the issue: without looking into it, sounds legit, hope to have time to look into this soon.
Thank you very much!