Explain Codes LogoExplain Codes Logo

How to de/serialize an immutable object without default constructor using ObjectMapper?

java
objectmapper
serialization
deserialization
Anton ShumikhinbyAnton Shumikhin·Feb 16, 2025
TLDR

Jackson's ObjectMapper handles immutable objects via the @JsonCreator and @JsonProperty annotations. Bind JSON fields to the constructor's parameters by tagging a constructor or static factory with @JsonCreator and each argument with @JsonProperty.

Example:

import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; public class Immutable { private final String data; // Constructor is at the "Bottom of the JSON Sea"! @JsonCreator public Immutable(@JsonProperty("data") String data) { this.data = data; // "Swallow" data from JSON like a whale 🐳 } // getters... } String json = "{\"data\":\"value\"}"; Immutable item = new ObjectMapper().readValue(json, Immutable.class); // ObjectMapper is your "JSON lifeguard"!

Key Solutions:

  • @JsonCreator: Allows initialization without a default constructor.
  • @JsonProperty: Bridges the gap between JSON and constructor parameters.

Tales from the Trenches on Serialization/Deserialization

ParameterNamesModule, Jackson's Swiss Army Knife

Simplify your annotations by registering the ParameterNamesModule. This will definitely save you a few keystrokes:

ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new ParameterNamesModule());

Spring Boot integers, you're off the hook; this happens automatically, no manual registration required.

The Lifeline: DeserializationProblemHandler

Enter the DeserializationProblemHandler, your lifesaver when encountering deserialization issues without changing class structure:

ObjectMapper mapper = new ObjectMapper(); mapper.addHandler(new DeserializationProblemHandler() { // Brace yourself, custom handling code is coming! });

Jackson 😍 Java Records

From Jackson 2.12.0 onwards and Java 16, leveraging Java records to serialize immutable data structures is a breeze:

public record ImmutableRecord(String data) {} // Serialization and deserialization, easier and sweeter than grandma's apple pie. ImmutableRecord record = mapper.readValue(json, ImmutableRecord.class);

All Builders on Deck!

Need more control? Use Builder Pattern:

class Immutable { // Build me up, Buttercup 🎵 // Our builder class goes here... }

The Builder Pattern flexes its muscles in more demanding serialization/deserialization scenarios.

Check Your Gear: Jackson Library Version

Take a moment to make sure you're packing a Jackson library version that supports these features. It's always good to know your gear before you dive in.

Protect the Treasure: Preserve Immutability

A public or protected default constructor is like a crack in the treasure chest. Refrain from it to maintain the immutability of the serialized object.

One ObjectMapper, One Mission

Consider implementing the Singleton Design Pattern to keep a single ObjectMapper instance across your application.

ObjectMapper on a Silver Platter: Dependency Injection

Why not consider Dependency Injection for ObjectMapper management? Your testing and modularity will thank you.

Calling All API Architects

You've got one job! Ensure consistent and intuitive API design. A well-structured design wards off confusion and streamlines serialization logic.

Problem-Specific Handling Techniques

Leverage DeserializationContext and JsonParser for controlling specific deserialization issues. They're your braces and bandages for any mishaps.

Many ObjectMappers: Friend or Foe?

Too many ObjectMapper instances causing a fuss? Go for an object pool; it’s pretty good at managing them.

Tailor-Made ObjectMappers

Factory methods can whip up ObjectMapper instances that meet your precise needs. Taylor Swift may not be involved, but you still get "Taylor-Made" configurations!

Befuddled by Java Records?

Here's a heads-up: Java records + getters with "get" or "is" prefixes = Serialization chaos! Navigate around this pitfall to safeguard serialization flow.

Keep It Real, Keep It Consistent

Your steadfast goal: retaining the serialized object's immutability and API integrity while battling the waves of application and dependency updates.