Explain Codes LogoExplain Codes Logo

Can't make Jackson and Lombok work together

java
lombok
jackson
builder-pattern
Alex KataevbyAlex Kataev·Jan 31, 2025
TLDR

Unlock Jackson’s power to serialize your Lombok-annotated classes with @Builder simply by adding @Jacksonized. Build bridges, not walls, by ensuring compatibility with @JsonDeserialize:

@Builder @Jacksonized @JsonDeserialize(builder = MyClass.MyClassBuilder.class) class MyClass { private String attribute; // Plan on using me everywhere! }

Equip yourself with Lombok 1.18.12+ as your toolkit and have necessary Jackson modules by your side. This is your recipe for a successful marriage of Lombok's builder pattern and Jackson's deserialization process!

Wrestling with immutable classes

An unchangeable relationship

Enter the ring of immutability with @Value from Lombok and @JsonDeserialize from Jackson:

@Value @AllArgsConstructor @JsonDeserialize(builder = ImmutableClass.ImmutableClassBuilder.class) public class ImmutableClass { // So, you are saying I can't change? String name; int age; }

Pro tip: Stick to this pattern for successful integration.

Builder gets a makeover

Fitting the square peg of Jackson's expectations into the round hole of Lombok's defaults isn't as complicated as it seems. Just meet in the middle with prefix:

@Value @Builder @Jacksonized @JsonDeserialize(builder = CustomizedBuilder.CustomizedBuilderBuilder.class) public class CustomizedBuilder { // I can be whatever you want me to be! String description; }

Breaking building blocks into simpler ones

Don't let your lombok.config feel left out. Lighten Jackson's load by managing your constructors:

lombok.anyConstructor.addConstructorProperties=true

Master the art of detailed configuration!

Setting yourself up for unexpected builder encounters

Have an ace up your sleeve for multiple builder implementations. Just override the annotation interceptor, like a boss:

@JacksonAnnotationsInside @JsonPOJOBuilder(withPrefix = "") @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface CustomizedJacksonized { // Look at me, I am the captain now! }

Default to Jackson for an effortless journey

Ease of use? Yes, please!

Embrace flexibility like it's your best friend. Just preset your ObjectMapper and leave the heavy lifting to Jackson:

ObjectMapper mapper = new ObjectMapper(); mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() { @Override public String findPOJOBuilderConfig(AnnotatedClass ac) { // This is my default territory. Stay clear! JsonPOJOBuilder.Value defaultConfig = super.findPOJOBuilderConfig(ac); return defaultConfig == null ? "": defaultConfig.withPrefix(); } });

Immutability yet again!

Just a small reminder of using @RequiredArgsConstructor for making immutable classes with only final fields:

@Value @RequiredArgsConstructor public class FinalFieldsClass { // Immortal until every instance of the class has been garbage collected! final String id; final String name; }

Simplify your JSON mappings

Don't leave JSON attributes mapping to luck. Use @JsonProperty to make sure everything is covered:

@Value public class ExplicitMapping { @JsonProperty("user_name") // Hi there, I used to be username! String username; @JsonProperty("user_age") // The more I grow, the more important I become. int age; }

Avoid the null-ity trap

Remember, @AllArgsConstructor with immutable objects might lead you straight into a trap of nulls. Hence, refrain from it.

Consistency is key

Unleash Jacksonized builders!

For a consistent and reliable model across your serialization and deserialization processes, bring together @Jacksonized and @Builder and @Value like a dream team:

@Value @Builder @Jacksonized public class ConsistentModel { // Stick together guys, we got a mission to fulfill! String feature; }

Cross the finish line with comprehensive testing

Make sure you crossed your Ts and dotted your Is by validating your serialization and deserialization:

class SerializationTest { @Test public void whenSerializeAndDeserialize_thenCorrect() { // Live long and prosper, dear tests! ConsistentModel model = new ConsistentModel("test"); ObjectMapper mapper = new ObjectMapper(); String serialized = mapper.writeValueAsString(model); ConsistentModel deserialized = mapper.readValue(serialized, ConsistentModel.class); assertEquals(model, deserialized); } }