It seems Jackson uses reflection to write additional attributes directly into fields even if a @JsonCreator constructor was used and the field is marked as final.
Example:
public class Test {
static class X {
final String s;
@JsonCreator
public X(@JsonProperty("a") String a) {
s = "s";
}
public String getS() {
return s;
}
}
@org.junit.Test
public void ds() throws Exception {
ObjectMapper om = new ObjectMapper();
X x = om.readValue("{`a`:``, `s`: `t`}".replace('`', '"'), X.class);
assertEquals("s", x.s);
}
}
The assert will fail with org.junit.ComparisonFailure: expected:<[s]> but was:<[t]>.
Is this behavior documented anywhere? Is there anyway to disable this globally?
Also, I think this is a very dangerous design: if there are some value that should be read-only to the client, this effectively allows the client to set them even if the class is well designed according to normal immutable class guidelines.
First: yes, Jackson allows deserialization of all visible and not just those for which @JsonCreator property exists. So it is possible to set a smaller set of properties via constructor, and others via setters or fields. This may be needed for some cases like cyclic types.
As to how to prevent use of s for deserialization here. An obvious way is to add @JsonIgnore for field, although if so you will also need @JsonProperty for getS() to avoid both being removed.
But there are other settings as well, in MapperFeature.
ALLOW_FINAL_FIELDS_AS_MUTATORS: if you disable this, final fields are never used directly for deserializationINFER_PROPERTY_MUTATORS: if you disable this, then neither fields nor setters are "pulled in" in cases where they are not otherwise visible (for fields, public is needed to be visible; for setters, even private is fine)So may want to disable one or both settings.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With