Jackson deserialization using a multi-parameter constructor
JSON deserialization is a common requirement in modern applications that interact with APIs and external systems. Handling different object creation patterns efficiently is key to building robust and maintainable Java applications. Let us delve into understanding Jackson deserialization with a multi-parameter constructor.
1. Introduction to Jackson Deserialization
Jackson is one of the most widely used libraries in Java for processing JSON data. It provides powerful features for converting JSON into Java objects (deserialization) and Java objects into JSON (serialization). By default, Jackson relies on a no-argument constructor and setter methods to populate object fields during deserialization. However, in modern Java applications, developers often prefer immutable objects, constructor-based initialization, and concise data models such as records. In such cases, the default approach is not sufficient. Jackson addresses this limitation by supporting annotations like @JsonCreator and @JsonProperty, which allow deserialization using multi-parameter constructors.
1.1 Setup and Dependency Configuration
To get started, you need to include the Jackson Databind dependency in your project. This module provides the ObjectMapper class, which is the core API used for reading and writing JSON. If you are using Maven, add the following dependency to your pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>stable__latest__version</version>
</dependency>
Make sure to replace stable__latest__version with the latest stable version available in Maven Central. Once added, your project will have access to all core Jackson features required for JSON deserialization.
2. Complete Code Example
This example demonstrates multiple Jackson deserialization approaches in a single program: default constructor, @JsonCreator with a multi-parameter constructor, Java records, and enum handling.
// Main.java
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
// Enum with custom deserialization
enum Status {
ACTIVE, INACTIVE;
@JsonCreator
public static Status fromValue(String value) {
return Status.valueOf(value.toUpperCase());
}
}
// 1. Default deserialization (uses constructor + setters)
class DefaultUser {
private String name;
private int age;
private Status status;
public DefaultUser() {} // required default constructor
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public Status getStatus() { return status; }
public void setStatus(Status status) { this.status = status; }
}
// 2. @JsonCreator with multi-parameter constructor (immutable)
class ImmutableUser {
private final String name;
private final int age;
@JsonCreator
public ImmutableUser(@JsonProperty("name") String name,
@JsonProperty("age") int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
// 3. Record (Java 14+)
record RecordUser(String name, int age) {}
public class Main {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
// Default deserialization
String json1 = "{ \"name\": \"John\", \"age\": 30, \"status\": \"active\" }";
DefaultUser user1 = mapper.readValue(json1, DefaultUser.class);
System.out.println("DefaultUser: " + user1.getName() + " - " + user1.getAge() + " - " + user1.getStatus());
// @JsonCreator deserialization
String json2 = "{ \"name\": \"Alice\", \"age\": 25 }";
ImmutableUser user2 = mapper.readValue(json2, ImmutableUser.class);
System.out.println("ImmutableUser: " + user2.getName() + " - " + user2.getAge());
// Record deserialization
String json3 = "{ \"name\": \"Bob\", \"age\": 40 }";
RecordUser user3 = mapper.readValue(json3, RecordUser.class);
System.out.println("RecordUser: " + user3.name() + " - " + user3.age());
}
}
2.1 Code Explanation
This program demonstrates multiple ways to perform JSON deserialization using Jackson in Java, including default deserialization, constructor-based deserialization with @JsonCreator, record support, and custom enum handling. The ObjectMapper class is the core component responsible for converting JSON strings into Java objects.
- The
Statusenum defines two constants:ACTIVEandINACTIVE. It uses a custom@JsonCreatormethod namedfromValue, which allows Jackson to convert incoming JSON values into enum constants. The method transforms the input string to uppercase before mapping it, enabling case-insensitive deserialization (for example, “active” becomesACTIVE). - The
DefaultUserclass demonstrates standard Jackson deserialization. It contains private fields along with a no-argument constructor and public getter and setter methods. During deserialization, Jackson first creates an instance using the default constructor and then populates the fields using the setter methods. This is the most common and straightforward approach but requires mutable objects. - The
ImmutableUserclass illustrates deserialization using a multi-parameter constructor. Since the fields are marked asfinaland there are no setters, Jackson cannot use the default approach. Instead, the constructor is annotated with@JsonCreator, and each parameter is annotated with@JsonPropertyto map JSON fields to constructor arguments. This approach is ideal for creating immutable objects. - The
RecordUseris a Java record, which provides a concise way to define immutable data carriers. Records automatically generate a canonical constructor, accessor methods, and other utility methods. Jackson natively supports records and uses the generated constructor for deserialization without requiring additional annotations. - In the
Mainclass, anObjectMapperinstance is created to handle the JSON processing. Three JSON strings are defined and deserialized into their respective classes. The first JSON string is mapped toDefaultUser, demonstrating setter-based population. The second is mapped toImmutableUser, showcasing constructor-based deserialization. The third is mapped toRecordUser, highlighting record support. Finally, the program prints the deserialized values to verify that each approach works as expected.
2.2 Code Output
DefaultUser: John - 30 - ACTIVE ImmutableUser: Alice - 25 RecordUser: Bob - 40
The output shows the successful deserialization of three different JSON inputs into their respective Java representations using different Jackson techniques. Each line corresponds to a specific deserialization strategy used in the program.
- In the first case, the JSON string containing
name,age, andstatusis deserialized into theDefaultUserclass. Jackson invokes the default constructor and then sets each field using the setter methods. Thestatusvalue “active” is passed to the enum’s@JsonCreatormethod, which converts it to uppercase and maps it toACTIVE. - In the second case, the JSON is deserialized into the
ImmutableUserclass. Since the class does not have a default constructor or setters, Jackson uses the constructor annotated with@JsonCreator. The@JsonPropertyannotations ensure that the JSON fields correctly map to the constructor parameters, allowing the object to be created with initialized final fields. - In the third case, the JSON is deserialized into the
RecordUserrecord. Jackson automatically detects the record structure and uses its canonical constructor to populate the fields. No additional annotations or configuration are required, making this approach concise and modern.
Overall, the output confirms that Jackson can handle mutable objects, immutable classes with multi-parameter constructors, and Java records seamlessly, while also supporting customized enum deserialization logic.
3. Conclusion
Jackson provides flexible ways to deserialize JSON into Java objects, even when using parameterized constructors. While default deserialization works for mutable objects, @JsonCreator is essential for immutable designs. Modern Java features like records further simplify this process, and enums can be customized for robust parsing. Choosing the right approach depends on your object design and application requirements.

