Java Serialization and Deserialization with Jackson: A Comprehensive Guide

Serialization and deserialization play a vital role in today’s software development landscape, particularly when dealing with APIs, persistent storage, or data exchanged over networks. These processes allow complex Java objects to be converted into a format suitable for transmission or storage (serialization) and then reconstructed back into objects (deserialization) when needed. In this comprehensive guide, we'll dive deep into how Java handles these tasks, with a special emphasis on the widely-used and versatile Jackson library. Jackson makes working with JSON in Java both efficient and intuitive, enabling developers to easily convert between Java objects and JSON data with minimal boilerplate code.

What is Serialization?

Serialization is the process of converting a Java object into a format suitable for storage or transmission, such as JSON, XML, or a byte stream. Specifically, in Java and JSON contexts, serialization refers to converting a Java object into a JSON string.

What is Deserialization?

Deserialization is the reverse process: converting a data format like JSON back into a Java object. It allows the reconstruction of Java objects from JSON input, enabling seamless data exchange between systems.

Why Jackson?

Jackson is one of the most popular Java libraries for JSON processing. It simplifies the serialization and deserialization of Java objects to and from JSON with minimal boilerplate code. In this article, we'll explore key Jackson annotations that facilitate these processes.

Example: Java Class and JSON File

Consider a simple Employee class and a corresponding JSON file, employee.json:

public class Employee {
public String name;
public String job;
public String company;
}
{
"name": "Reacher",
"job": "leader",
"company": "abacus"
}


Serializing a Java Object to JSON

To serialize an Employee object into a JSON string:
public static void main(String[] args) throws IOException {
Employee employee = new Employee("Tom", "Creator", "Mango");
ObjectMapper objectMapper = new ObjectMapper();
String jsonString = objectMapper.writeValueAsString(employee);
System.out.println(jsonString);
}
The output of this method is 
{
"name": "Tom",
"job": "Creator",
"company": "Mango"
}


Deserializing JSON to Java Object

To deserialize employee.json back to an Employee object:
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Employee employee = objectMapper.readValue(new File("employee.json"),Employee.class);

System.out.println("Name: " + employee.name);
System.out.println("Job: " + employee.job);
System.out.println("Company: " + employee.company);
}


Handling Constructors During Deserialization

To deserialize JSON into a Java object, Jackson requires either:

1. A default (no-argument) constructor.
2. A constructor annotated with @JsonCreator and fields mapped using @JsonProperty.
@JsonCreator
public Employee(
@JsonProperty("name") String name,
@JsonProperty("job") String job,
@JsonProperty("company") String company) {
this.name = name;
this.job = job;
this.company = company;
}


Working with Private Fields

By default, Jackson works with public fields. To use private fields, we can:

1. Annotate the class with @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
2. Provide public getters and setters
3. Annotate fields with @JsonProperty

Failure to handle private fields appropriately may result in exceptions like UnrecognizedPropertyException or InvalidDefinitionException.


Jackson Annotations for Advanced Control

@JsonIgnoreProperties

During deserialization if Jackson does not recognize a field in the JSON because it does not exist in the Employee class, we will get UnrecognizedPropertyException, to get rid of this exception we use a class-level annotation @JsonIgnoreProperties

 employee.json:
{
"name": "Reacher",
"job": "leader",
"company": "abacus",
"idNumber": "926",
"nickName": "Jack"
}
We can also specify fields explicitly:
@JsonIgnoreProperties(value = {"idNumber","nickName"})
public class Employee {
private String name;
private String job;
private String company;
}
or we can use attribute ignoreUnknown = true
@JsonIgnoreProperties(ignoreUnknown = true)
public class Employee {
private String name;
private String job;
private String company;
}
If we want to ignore a field when reading JSON but include it when writing JSON, use allowGetters = true.
@JsonIgnoreProperties(value = "name", allowGetters = true)
public class Employee {
private String name;
private String job;
private String company;
}
In the above case during the deserialization, the value of the field will be null even if it is available in JSON.

If we want to ignore a field during serialization but include during deserialization, use allowSetters = true.
@JsonIgnoreProperties(value = "name", allowSetters = true)
public class Employee {
private String name;
private String job;
private String company;
}
In this case even if the field name is passed from Employee object it will be ignored in JSON during serialization. The result of serialization will be.
{
"job": "leader",
"company": "abacus"
}


@JsonIgnore

public class Employee {
@JsonIgnore
private String name;
private String job;
private String company;
}
@JsonIgnore excludes a field from serialization (Java Object → JSON) and deserialization (JSON → Java Object). In simple terms:  The field will be ignored when converting to/from JSON. In the above example name will not be included in the output JSON during serialization and the value of name during deserialization will be null even if available.


@JsonIgnoreType

@JsonIgnoreType is used to ignore an entire class/type during serialization and deserialization. Consider the following Employee class. Here if we want ignore the Address object during serialization and deserialization, we can annotate the Address class with @JsonIgnoreType as shown.
public class Employee {
private String name;
private String job;
private String company;
private Address address;
}

@JsonIgnoreType
public class Address {
private String city;
private String street;
}

@JsonProperty

@JsonProperty is used to:
1. Define custom JSON property names for fields or methods.
2. Map JSON properties to Java fields when the names don’t match.

public class Employee {
@JsonProperty("firstName")
private String name;
private String job;
private String company;
}


@JsonAlias

@JsonAlias allows us to specify multiple alternative names (aliases) for a JSON property during deserialization. It helps when incoming JSON data might have different field names, but we want to map them all to a single Java field.

public class Employee {
@JsonAlias({"firstName", "empName", "name"})
private String name;
private String job;
private String company;
}

Having a solid grasp of serialization and deserialization in Java—particularly when using the Jackson library—is key to managing data effectively in modern applications. Jackson provides a rich set of annotations such as @JsonProperty, @JsonAlias, @JsonIgnoreProperties, and many more, which empower developers to precisely control how Java objects are mapped to and from JSON. These annotations make it easy to handle complex data structures, accommodate changing API contracts, and maintain clean, readable code. By leveraging Jackson's flexibility and depth, developers can build robust, scalable, and maintainable Java applications that interact seamlessly with external systems and data formats.



Post a Comment

Previous Post Next Post