Jackson Deserialize Record with default value for optional fields

Viewed 466

Assume a JSON structure with multiple optional fields. With classes, you can do something like

    public static final class Foo {
        @JsonProperty("x")
        private int x = 1;

        @JsonProperty("y")
        private int y = 2;

        @JsonProperty("z")
        private int z = 3;
        
    }

which defines default values for the fields in case it is not present in the provided json. Can this be done with records as well?

    public record Foo(int x, int y, int z) {

    }

Constructor overloading is obviously not an option, and as far as I know you could only have a single @JsonCreator annotation anyway.

A custom deserializer should do the trick, but is there any other way, like an annotation that provides a default value to use in the constructor of the record in case it is not provided in the json?

1 Answers

Jackson does not support defining default values for nulls.
There is no annotation to set default value.
You can set default value only on java class level.
There is open Jackson issue for that functionality.

Solution is to define only one constructor with properties initialization logic in case nulls. Record is immutable, fields filling performs only via constructor. Only in the constructor, you can define default values for record fields.

public record Foo(Integer x, Integer y, Integer z) {
    public Foo(Integer x, Integer y, Integer z) {
        this.x = x == null ? 1 : x;
        this.y = y == null ? 2: y;
        this.z = z == null ? 3: z;
    }
}

Unit test:

    @Test
    public void test() throws Exception {
        int xDefault = 1;
        int yDefault = 2;
        int zDefault = 3;

        String json = "{ \"x\": 11, \"y\":22, \"z\":33 }";
        ObjectMapper objectMapper = new ObjectMapper();
        Foo foo = objectMapper.reader().readValue(json, Foo.class);

        Assert.assertEquals(11, (int) foo.x());
        Assert.assertEquals(22, (int) foo.y());
        Assert.assertEquals(33, (int) foo.z());

        String json2 = "{ \"x\": 11, \"y\":22}";
        Foo foo2 = objectMapper.reader().readValue(json2, Foo.class);

        Assert.assertEquals(11, (int) foo2.x());
        Assert.assertEquals(22, (int) foo2.y());
        Assert.assertEquals(zDefault, (int) foo2.z());

        String json3 = "{ }";
        Foo foo3 = objectMapper.reader().readValue(json3, Foo.class);

        Assert.assertEquals(xDefault, (int) foo3.x());
        Assert.assertEquals(yDefault, (int) foo3.y());
        Assert.assertEquals(zDefault, (int) foo3.z());
    }
Related