Immutable Lombok annotated class with Jackson
add ConstructorProperties:
- Create a
lombok.config
file in an appropriate location with the line:lombok.anyConstructor.addConstructorProperties = true
- Add lombok
@Value
annotation to your class to make it immutable
Then serialization and deserialization by Jackson works as expected.
This method:
- meets the criteria
- has less boilerplace than the previous top answer
- works on v1.16.20 (January 9th, 2018) and later
Edit: 2020-08-16
- Note: Using
@Builder
with@Value
causes this solution to fail. (Thanks to comment from @guilherme-blanco below.)However, if you also add e.g.@AllArgsConstructor
it does still work as expected.
Edit: 2021-08-19
- Note: When you add or change a
lombok.config
file the change is not picked up unless you do a rebuild (clean then build). I have been caught out by this a few times. @Jacksonized
annotation solution is another method to achieve the desired outcome for the specific classes annotated. However, I personally prefer not to need to remember to annotate every class used for deserialization. Usinglombok.config
removes this overhead.
As of 15 October 2020 (Lombok v1.18.16), you should simply be able to use the @Jacksonized
annotation.
@Jacksonized @Builder@JsonIgnoreProperties(ignoreUnknown = true)public class JacksonExample { private List<Foo> foos;}
As described in the linked documentation, this annotation:
- configures Jackson to use the builder for deserialisation,
- copies field-specific configuration down from the annotated class to the generated builder (e.g.
@JsonIgnoreProperties
), and - aligns the Jackson prefix used on builder methods (e.g.
builder().withField(field)
vsbuilder.field(field)
to the one configured in Lombok.
You can use Lombok's @Builder
annotation to generate a builder for your immutable POJO class.But making the Lombok-generated builder usable by Jackson's deserialization is somewhat tricky.
- You need to annotate your POJO class with
@JsonDeserialize(builder = ...)
to tell Jackson which is the builder class to use. - You need to annotate the builder class with
@JsonPOJOBuilder(withPrefix = "")
to tell Jackson that its setter-methods do not start withwith
.
Example:
An immutable POJO class:
@Data@Builder(builderClassName = "PointBuilder")@JsonDeserialize(builder = Point.PointBuilder.class)public class Point { private final int x; private final int y; @JsonPOJOBuilder(withPrefix = "") public static class PointBuilder { // Lombok will add constructor, setters, build method }}
Here is a JUnit test to verify the serialization/deserialization:
public class PointTest extends Assert { private ObjectMapper objectMapper = new ObjectMapper(); @Test public void testSerialize() throws IOException { Point point = new Point(10, 20); String json = objectMapper.writeValueAsString(point); assertEquals("{\"x\":10,\"y\":20}", json); } @Test public void testDeserialize() throws IOException { String json = "{\"x\":10,\"y\":20}"; Point point = objectMapper.readValue(json, Point.class); assertEquals(new Point(10, 20), point); }}