Jackson: the final field written after being set by @JsonCreator?
It seems that Jackson is using reflection to write additional attributes directly to the fields, even if a constructor was used @JsonCreator
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 assertion will fail with org.junit.ComparisonFailure: expected:<[s]> but was:<[t]>
.
Is this behavior documented anywhere? Should I turn this off globally at all?
Also, I think this is a very dangerous design: if there is some value that should be read-only to the client, it actually allows the client to set them even if the class is well designed to fit with normal immutable classes.
source to share
First: yes, Jackson allows you to deserialize all visible ones, not just those for which the property exists @JsonCreator
. Thus, it is possible to set a smaller set of properties through the constructor and others through setters or fields. This may be required for some cases, such as looping types.
How to prevent use s
for deserialization here. The obvious way is to add @JsonIgnore
for the field, although you will also need @JsonProperty
to for getS()
that to avoid removing them.
But there are other settings, in MapperFeature
.
-
ALLOW_FINAL_FIELDS_AS_MUTATORS
: if you disable this, the fieldsfinal
will never be used directly for deserialization -
INFER_PROPERTY_MUTATORS
: if you disable this, then neither fields nor setters will be "pulled in" in cases where they are not visible otherwise (for fields,public
you need to be visible, for setters, evenprivate
good)
So you can turn off one or both settings.
source to share