Jackson deserialization bypassing trailing fields
Here's the code
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.ToString;
public class Main {
public static void main(String[] args) throws Exception {
Fields f1 = new Fields(1);
System.out.println(f1);
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(f1);
System.out.println(str);
Fields f2 = mapper.readValue(str, Fields.class);
System.out.println(f2);
}
@Data
@ToString
public static class Fields {
private final long value1;
private final long value2;
public Fields(@JsonProperty("blah") long value) {
this.value1 = value++;
this.value2 = value++;
System.out.println(this);
}
}
}
Output
Main.Fields(value1=1, value2=2)
Main.Fields(value1=1, value2=2)
{"value1":1,"value2":2}
Main.Fields(value1=0, value2=1)
Main.Fields(value1=1, value2=2)
My questions:
- Why did Jackson change the private finite fields that have no setters after completing his construction? If necessary, how do I disable it?
- If jackson can set fields directly, why would you want to annotate the constructor with @JsonProperty? (Removing @JsonProperty from fields results in an error and I didn't even need to comment on the correct properties)
thank
source to share
Why is it required to annotate the constructor annotation with @JsonProperty?
This is not true. An accessible constructor is required. You can either have a parameterless constructor
public Fields() {
this.value1 = 0;
this.value2 = 0;
System.out.println("cons: " + this);
}
(which will make sure to initialize the fields as they are final
), or you can create a constructor that Jackson will try to solve based on the declared name @JsonProperty
. Please note that JsonProperty#required
by default false
.
Why did Jackson change the private finite fields that have no setters after his completion? If it's intended, how do I turn it off?
Because it is possible. This way you can use deserialized immutable types. There is no built-in way that I know through which you can disable this feature.
source to share
Why did Jackson change the private finite fields that have no setters after completing his construction? If necessary, how do I disable it?
You can set the property to a MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS
value false
(the default is true
) when configuring your mapper.
Example:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Temp {
private static final String RAW = "{\"value1\": \"aabbcc\",\"value2\":\"zzzzz\"}";
public static void main(String[] args) throws Exception {
System.out.println(new ObjectMapper().readValue(RAW, TestClass.class));
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // you will receive UnrecognizedPropertyException without this line
mapper.configure(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS, false);
System.out.println(mapper.readValue(RAW, TestClass.class));
}
public static class TestClass {
private final String value1;
private final String value2;
@JsonCreator
public static TestClass createTestClass(
@JsonProperty("value1") String value1,
@JsonProperty("blah") String value2) {
return new TestClass(value1, value2);
}
private TestClass(String value1, String value2) {
this.value1 = value1;
this.value2 = value2;
}
public String getValue1() {
return value1;
}
public String getValue2() {
return value2;
}
@Override
public String toString() {
return "TestClass{" + "value1=" + value1 + ", value2=" + value2 + '}';
}
}
}
Output:
TestClass{value1=aabbcc, value2=zzzzz}
TestClass{value1=aabbcc, value2=null}
source to share