Java – Jackson deserialization circumventing final fields

immutabilityjacksonjavajsonlombok

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 are:

  • Why did jackson modify private final fields that do not have setters after finish constructing it? If this is intended, how do I turn it off?
  • If jackson is able to set fields directly, why is it required that I annotate the constructor with @JsonProperty? (Removing @JsonProperty from Fields results in error; and I didn't even need to annotate with correct properties)

Thank you

Best Answer

Why is it required that I annotate the constructor with @JsonProperty?

It's not. What is required is an accessible constructor. You can either have a parameterless constructor

public Fields() {
    this.value1 = 0;
    this.value2 = 0;
    System.out.println("cons: " + this);
}

(that necessarily initializes the fields since they are final) or you can have a constructor that Jackson will try to resolve based on the declared @JsonProperty name. Note that JsonProperty#required is false by default.

Why did jackson modify private final fields that do not have setters after finish constructing it? If this is intended, how do I turn it off?

Because it can. It thus allows you to use immutable types with deserialization. There is no built-in way that I know of through which you can disable this feature.