Immutable dto in jackson

java

Immutability and functional programming are hot right now. But how to achieve immutability with objects deserialized from json? Luckily there is pretty old feature introduced in jackson 2.7.0 which uses constructor based object creation and uses @ConstructorProperties (introduced in java7).

With this approach you can easily deserialize immutable objects from JSON.

import java.beans.ConstructorProperties;
import com.fasterxml.jackson.annotation.JsonAutoDetect;

// instead of JsonAutoDetect you can generate getters. It's required for serialization
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class PointVanillaJava {
  private final int x;
  private final int y;

  @ConstructorProperties({"x", "y"})
  public PointVanillaJava(int x, int y) {
    this.x = x;
    this.y = y;
  }
}

Now Jackson will be able to create object instance using properly annotated constructor. It’s all good but it is very verbose code. We can use Lombok which has @RequiredArgsConstructor and it adds @ConstructorProperties annotation to generated code automatically. With this in mind, we can use Lombok to generate boring stuff:

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class PointLombok {
  private final int x;
  private final int y;
}

Looks good just let’s make sure everything works as expected:

@Unroll
def "should deserialize point #pointClass.simpleName from json"() {
  when:
  final point = objectMapper.readerFor(pointClass).readValue("""{"x": 10, "y":20}""")

  then:
  point.x == 10
  point.y == 20

  where:
    pointClass << [
      PointLombok.class,
      PointVanillaJava.class
    ]
}

When I first saw when an immutable entity with @RequiredArgsConstructor is properly deserialized from JSON I was really surprised and had one of those wtf moments “how it’s working? how is it working?” Then I setup few breakpoints and finally figured out what’s going on and how it’s working :)

source code

15 Apr 2017 #interesting