Spring — Immutable objects & JSON De-serialization

Udayabharathi Thiagarajan
3 min readMay 10, 2021
Photo by Michael Dziedzic on Unsplash

Good day readers! We often tend to create POJOs for many use cases in our applications. Some of these use cases are always like create once and utilize continuously based on our business logic. For example, REST request body/parameters, MQ message objects, response of some REST API call and some cases. If we strictly follow OOPs, then we might definitely make all the attributes in our POJO final. But there is a challenge in doing that.

The challenge:
Generally, the object mapper class of our choice is being utilized by Spring to convert the application/json message/request/response to our defined POJO. If you have seen how these object mappers work, then you might see a flaw in the below code snippets.

User.java
UserController.java

The above code snippets will fail on runtime when the POST /api/users is called with some input.

If you have used Jackson as object mapper, then you will face the following issue during runtime.

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class io.ud.project.dto.User]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: {"name":"Darth Vader","email":"darth.vader@empire.sw","type":"Sith","age":22}; line: 1, column: 2]

This issue occurs because, object mappers generally use no argument or default constructor to de-serialize the JSON as a Java object.

The reason why object mappers fails without default constructor:
Object mappers basically utilize setters to match the incoming attribute using Java reflections. But this is not possible with constructors since reflection doesn’t allow you to fetch parameter’s name. So object mappers will not have the context or idea on what order it needs to inject the value into our newly created POJO.

The resolution:
This issue can be resolved by using @java.beans.ConstructorProperties annotation on top of our all argument constructor. An example code snippet below.

Modified User.java

Lombok Support:
You might wonder, whether we need to start defining the constructor which leads to boiler plate coding. But the answer is, definitely no! Lombok supports @java.beans.ConstructorProperties. You need to enable the following property in a configuration file with a name lombok.config. Don’t worry, if you did not find this file in your existing project. This file can be created and placed under any directories except resources directory. I generally prefer /src/main/java.

lombok.config

Once this configuration is enabled, we can either utilize @lombok.AllArgsConstructor or even @lombok.Value annotation to reduce the boiler plate code.

Using @lombok.Value — User.java

References:
1. Lombok Constructors
2. java.bean.ConstructorProperties

--

--