Popular Searches
Popular Course Categories
Popular Courses

Handling JSON Serialization and Deserialization

Handling JSON Serialization and Deserialization

RESTful Web Services and API Development

Handling JSON Serialization and Deserialization in Java and Spring Boot

Course: Advanced Java Training

Topic: Handling JSON Serialization and Deserialization

Course Link: Advanced Java Training Course

Demo Link: Register for a Free Course Demo

Website: JustAcademy


1. Introduction to JSON Serialization and Deserialization

JSON (JavaScript Object Notation) is a lightweight, text-based data format commonly used to exchange information between web applications, REST APIs, mobile applications, and backend services. It represents data using objects, arrays, strings, numbers, Boolean values, and null.

In Java applications, data is generally represented using classes and objects. When an application needs to send Java objects to another system, it often converts those objects into JSON. When JSON data is received, the application converts it back into Java objects.

These two processes are called serialization and deserialization. In Spring Boot REST applications, Jackson is a commonly used JSON processing library, and Spring Boot automatically configures JSON support when the appropriate dependencies are available.

What Is Serialization?

Serialization is the process of converting a Java object into a format suitable for storage or transmission, such as a JSON string.

Java Object → JSON String

What Is Deserialization?

Deserialization is the reverse process. It converts JSON data into a Java object that an application can use.

JSON String → Java Object

Example

Consider a Java object representing a student:

Student student = new Student(101, "Rahul", "[email protected]");

During serialization, the object can be converted into JSON similar to the following:

{
  "id": 101,
  "name": "Rahul",
  "email": "[email protected]"
}

When this JSON is received by another Java application, deserialization can convert it back into a Student object.

2. Learning Objectives

  • Understand JSON and its role in Java applications.
  • Differentiate between serialization and deserialization.
  • Learn how to use the Jackson library for JSON processing.
  • Convert Java objects into JSON strings and JSON strings into Java objects.
  • Handle JSON request bodies and responses in Spring Boot REST APIs.
  • Customize JSON output using annotations.
  • Work with collections, nested objects, dates, and optional fields.
  • Handle invalid JSON and data conversion errors.
  • Apply security and validation best practices.

3. Understanding the JSON Data Format

JSON uses a straightforward structure that can be understood by many programming languages. It is frequently used in HTTP request bodies and API responses.

3.1 JSON Objects

A JSON object is a collection of key-value pairs enclosed in curly braces.

{
  "id": 101,
  "name": "Rahul",
  "active": true
}

3.2 JSON Arrays

A JSON array contains an ordered collection of values enclosed in square brackets.

[
  {
    "id": 101,
    "name": "Rahul"
  },
  {
    "id": 102,
    "name": "Priya"
  }
]

3.3 Common JSON Data Types

JSON Type Example Java Equivalent
String "Rahul" String
Number 101 int, long, double, BigDecimal
Boolean true boolean or Boolean
Object {"city":"Mumbai"} Java object or Map
Array [1,2,3] Array, List, or another collection
Null null null reference

4. What Is Jackson?

Jackson is a widely used Java library for processing JSON. It provides functionality for converting Java objects into JSON and JSON into Java objects. Spring Boot integrates JSON processing with its HTTP message conversion system.

Jackson provides classes and annotations that support object mapping, property customization, collection processing, date formatting, and custom serialization logic.

Important Jackson Components

Component Purpose
ObjectMapper Common Jackson 2 API for reading and writing JSON.
JsonMapper Jackson 3 mapper API used by current Spring Boot configurations.
JsonNode Represents JSON as a tree structure.
@JsonProperty Customizes the JSON property name.
@JsonIgnore Excludes a property from normal JSON mapping.
@JsonFormat Controls formatting for supported values such as dates.
@JsonInclude Controls which properties are included in serialized JSON.
JsonSerializer Jackson 2 base class for custom serialization.
JsonDeserializer Jackson 2 base class for custom deserialization.

Version note: The main examples in these notes use the familiar Jackson 2 API with com.fasterxml.jackson imports, commonly found in Spring Boot 3 projects. Spring Boot 4 uses Jackson 3 by default, whose core APIs use different package names and some renamed classes. Follow the API version managed by your project's Spring Boot dependencies.

5. Setting Up JSON Support in a Java Project

In a Spring Boot web application, JSON support is generally provided through the web starter and its managed dependencies.

5.1 Maven Dependency

For a typical Spring Boot REST application, add the web starter to your Maven project:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Use the Spring Boot parent or dependency management configured for your project so compatible library versions are selected automatically. Avoid adding conflicting Jackson versions without a specific reason.

5.2 Gradle Dependency

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
}

When the required JSON mapper is present, Spring Boot can normally configure it automatically. You do not need to manually create a mapper for every controller.

6. Creating a Java Model Class

A model class represents the data that an application processes. Jackson maps JSON properties to compatible Java properties or constructor parameters, depending on the class structure and configuration.

Student.java

public class Student {
    private int id;
    private String name;
    private String email;

    public Student() {
    }

    public Student(int id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

This class includes a no-argument constructor, fields, and getters and setters. This conventional structure works well with common Jackson 2 bean-mapping configurations.

7. Serialization Using ObjectMapper

Serialization converts a Java object into JSON. In Jackson 2, the ObjectMapper class provides methods such as writeValueAsString() and writeValue().

7.1 Converting an Object to a JSON String

import com.fasterxml.jackson.databind.ObjectMapper;

public class SerializationExample {
    public static void main(String[] args) throws Exception {
        Student student = new Student(
            101,
            "Rahul",
            "[email protected]"
        );

        ObjectMapper mapper = new ObjectMapper();

        String json = mapper.writeValueAsString(student);

        System.out.println(json);
    }
}

Expected output:

{"id":101,"name":"Rahul","email":"[email protected]"}

7.2 Pretty-Printed JSON

Pretty printing makes JSON easier to read during debugging and development.

String json = mapper
    .writerWithDefaultPrettyPrinter()
    .writeValueAsString(student);

System.out.println(json);

Example output:

{
  "id" : 101,
  "name" : "Rahul",
  "email" : "[email protected]"
}

7.3 Writing JSON to a File

import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;

public class WriteJsonFile {
    public static void main(String[] args) throws Exception {
        Student student = new Student(
            101,
            "Rahul",
            "[email protected]"
        );

        ObjectMapper mapper = new ObjectMapper();

        mapper.writerWithDefaultPrettyPrinter()
              .writeValue(new File("student.json"), student);
    }
}

The writeValue() method writes JSON to a destination such as a file or output stream. Ensure that the application has the required file permissions and that sensitive information is not accidentally written to an exposed location.

8. Deserialization Using ObjectMapper

Deserialization converts JSON text into a Java object. Jackson reads the JSON properties and maps compatible values to the target Java type.

8.1 Converting JSON to a Java Object

import com.fasterxml.jackson.databind.ObjectMapper;

public class DeserializationExample {
    public static void main(String[] args) throws Exception {
        String json =
            "{\"id\":101,\"name\":\"Rahul\","
            + "\"email\":\"[email protected]\"}";

        ObjectMapper mapper = new ObjectMapper();

        Student student = mapper.readValue(
            json,
            Student.class
        );

        System.out.println(student.getId());
        System.out.println(student.getName());
        System.out.println(student.getEmail());
    }
}

Expected output:

101
Rahul
[email protected]

8.2 Reading JSON from a File

Student student = mapper.readValue(
    new File("student.json"),
    Student.class
);

Jackson also supports reading JSON from supported streams and other input sources. For large inputs, consider streaming or incremental parsing rather than loading an unnecessarily large JSON document into memory.

9. Serialization vs. Deserialization

Feature Serialization Deserialization
Direction Java object to JSON JSON to Java object
Purpose Prepare data for transmission or storage Make received data available to Java code
Common Jackson 2 method writeValueAsString() readValue()
Typical REST use Returning an API response Reading an API request body
Potential errors Unsupported values or mapping configuration problems Malformed JSON, type mismatches, or missing required data

10. Handling JSON Automatically in Spring Boot REST APIs

Spring MVC uses HTTP message converters to transform request and response bodies. With JSON support configured, a controller can accept a Java object using @RequestBody and return an object that Spring serializes into JSON.

10.1 Creating a REST Controller

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StudentController {

    @GetMapping("/students/sample")
    public Student getStudent() {
        return new Student(
            101,
            "Rahul",
            "[email protected]"
        );
    }

    @PostMapping("/students")
    public Student createStudent(
            @RequestBody Student student) {
        return student;
    }
}

10.2 Understanding the GET Response

When a client sends a GET request to /students/sample, Spring serializes the returned Student object into a JSON response, provided JSON is the negotiated response format.

{
  "id": 101,
  "name": "Rahul",
  "email": "[email protected]"
}

10.3 Understanding the POST Request

A client can send a JSON request body to /students:

{
  "id": 102,
  "name": "Priya",
  "email": "[email protected]"
}

Spring's message converter deserializes the body into a Student object before passing it to the controller method. The example returns the same object as JSON; it does not persist the student in a database.

Important: A production application should normally validate incoming data, use suitable request DTOs, call a service layer, and persist data through a repository where needed.

11. Using @RequestBody and @ResponseBody

11.1 @RequestBody

The @RequestBody annotation tells Spring MVC to read the HTTP request body and convert it into the declared Java type.

@PostMapping("/students")
public Student create(
        @RequestBody Student student) {
    return student;
}

11.2 @ResponseBody

The @ResponseBody annotation tells Spring MVC to write a method's return value to the HTTP response body using a suitable message converter.

@PostMapping("/students/echo")
@ResponseBody
public Student echo(@RequestBody Student student) {
    return student;
}

@RestController combines @Controller and @ResponseBody, so methods in a REST controller return response bodies by default.

12. Customizing JSON Property Names

Sometimes the property names used in a Java class differ from the names expected by an API. Jackson's @JsonProperty annotation allows you to specify a JSON property name.

import com.fasterxml.jackson.annotation.JsonProperty;

public class Employee {
    @JsonProperty("employee_id")
    private int id;

    @JsonProperty("full_name")
    private String name;

    public Employee() {
    }

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

The JSON representation can use names such as employee_id and full_name rather than id and name.

{
  "employee_id": 501,
  "full_name": "Anita Sharma"
}

Use consistent naming conventions across APIs to make integration easier.

13. Excluding Fields with @JsonIgnore

The @JsonIgnore annotation excludes a property from normal Jackson mapping. This is useful when a field should not be exposed in an API representation.

import com.fasterxml.jackson.annotation.JsonIgnore;

public class User {
    private String username;

    @JsonIgnore
    private String internalNote;

    public User() {
    }

    public User(String username, String internalNote) {
        this.username = username;
        this.internalNote = internalNote;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getInternalNote() {
        return internalNote;
    }

    public void setInternalNote(String internalNote) {
        this.internalNote = internalNote;
    }
}

When serialized with the default Jackson 2 configuration, the ignored property is normally omitted. Annotation behavior can be affected by configuration and access rules, so verify the actual JSON output in tests.

Security note: Never rely on serialization annotations alone as the entire security strategy for passwords, tokens, or other sensitive data. Prefer dedicated response DTOs that do not contain secrets.

14. Omitting Null Values with @JsonInclude

Sometimes APIs should omit fields whose values are null rather than returning those fields with explicit null values. The @JsonInclude annotation supports this behavior.

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Product {
    private int id;
    private String name;
    private String description;

    public Product() {
    }

    public Product(int id, String name, String description) {
        this.id = id;
        this.name = name;
        this.description = description;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

If the description is null, the serialized JSON can omit that property:

{
  "id": 201,
  "name": "Keyboard"
}

NON_NULL excludes null-valued properties. Other inclusion options can be used when the API needs different rules for empty or default values.

15. Working with Collections and JSON Arrays

JSON arrays commonly represent lists of records. Jackson can serialize Java arrays and collections and can deserialize JSON arrays into suitable collection types.

15.1 Serializing a List

import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Arrays;
import java.util.List;

List<Student> students = Arrays.asList(
    new Student(101, "Rahul", "[email protected]"),
    new Student(102, "Priya", "[email protected]")
);

ObjectMapper mapper = new ObjectMapper();

String json = mapper.writeValueAsString(students);

System.out.println(json);

15.2 Deserializing a JSON Array

When reading a JSON array into a generic collection, use a type reference so Jackson knows the element type.

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;

String json =
    "[{\"id\":101,\"name\":\"Rahul\","
    + "\"email\":\"[email protected]\"},"
    + "{\"id\":102,\"name\":\"Priya\","
    + "\"email\":\"[email protected]\"}]";

ObjectMapper mapper = new ObjectMapper();

List<Student> students = mapper.readValue(
    json,
    new TypeReference<List<Student>>() {}
);

for (Student student : students) {
    System.out.println(student.getName());
}

Using TypeReference preserves the generic collection element type that would otherwise be lost through Java type erasure.

16. Handling Nested JSON Objects

Real-world JSON often contains nested objects. Jackson can map nested JSON structures to Java classes when the model structure and property names are compatible.

16.1 Address Class

public class Address {
    private String city;
    private String country;

    public Address() {
    }

    public Address(String city, String country) {
        this.city = city;
        this.country = country;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }
}

16.2 Student Class with Address

public class Student {
    private int id;
    private String name;
    private Address address;

    public Student() {
    }

    public Student(int id, String name, Address address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}

16.3 Example Nested JSON

{
  "id": 101,
  "name": "Rahul",
  "address": {
    "city": "Mumbai",
    "country": "India"
  }
}

The address property maps to an Address object. This approach helps represent structured data without flattening every property into a single class.

17. Handling Dates and Time Values

Date and time serialization requires attention because Java date-time types can have different meanings. A date such as a birthday differs from a timestamp that identifies a specific instant.

For modern Java applications, types from java.time, such as LocalDate, LocalDateTime, and Instant, are generally preferable to legacy date types.

17.1 Example with LocalDate

import java.time.LocalDate;

public class Employee {
    private int id;
    private String name;
    private LocalDate joiningDate;

    public Employee() {
    }

    public Employee(int id, String name, LocalDate joiningDate) {
        this.id = id;
        this.name = name;
        this.joiningDate = joiningDate;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public LocalDate getJoiningDate() {
        return joiningDate;
    }

    public void setJoiningDate(LocalDate joiningDate) {
        this.joiningDate = joiningDate;
    }
}

For Jackson 2, Java time support is commonly provided by the jackson-datatype-jsr310 module. Spring Boot's managed dependencies and auto-configuration normally handle the required setup for typical web applications.

An ISO-style date may be represented as:

{
  "id": 501,
  "name": "Anita",
  "joiningDate": "2026-10-24"
}

The exact representation depends on the mapper configuration. Define and document the expected format for API contracts, especially when dealing with time zones or timestamps.

18. Working with Enums in JSON

Java enums represent a fixed set of values, such as order statuses or user roles. Jackson commonly serializes enum values as strings based on their names.

public enum OrderStatus {
    PENDING,
    PROCESSING,
    COMPLETED,
    CANCELLED
}

Example JSON:

{
  "orderId": 1001,
  "status": "PROCESSING"
}

Ensure that incoming enum values match the expected values or are handled through an intentional mapping strategy. Unknown or misspelled values can cause deserialization errors depending on the mapper configuration.

19. Custom Serialization

Custom serialization is useful when the default object-to-JSON mapping does not meet an application's requirements. Examples include masking selected data, changing a complex value into a simpler representation, or applying a specific output format.

For Jackson 2, a custom serializer extends JsonSerializer and implements its serialization method.

19.1 Example Custom Serializer

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;

public class MoneySerializer extends JsonSerializer<Double> {

    @Override
    public void serialize(
            Double value,
            JsonGenerator generator,
            SerializerProvider serializers) throws IOException {

        generator.writeString(
            String.format(java.util.Locale.ROOT, "%.2f", value)
        );
    }
}

19.2 Applying the Custom Serializer

import com.fasterxml.jackson.databind.annotation.JsonSerialize;

public class Invoice {
    private int id;

    @JsonSerialize(using = MoneySerializer.class)
    private Double amount;

    public Invoice() {
    }

    public Invoice(int id, Double amount) {
        this.id = id;
        this.amount = amount;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Double getAmount() {
        return amount;
    }

    public void setAmount(Double amount) {
        this.amount = amount;
    }
}

The amount can be serialized as a string with two decimal places, for example "1250.50". Be aware that representing numbers as strings changes the JSON data type, so follow the API contract and use a decimal type such as BigDecimal for financial calculations where appropriate.

20. Custom Deserialization

Custom deserialization allows an application to transform incoming JSON values into Java values using custom rules. It is useful when the external data format differs from the internal model.

20.1 Example Custom Deserializer

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;

public class TrimmedStringDeserializer
        extends JsonDeserializer<String> {

    @Override
    public String deserialize(
            JsonParser parser,
            DeserializationContext context) throws IOException {

        String value = parser.getValueAsString();

        return value == null ? null : value.trim();
    }
}

20.2 Applying the Deserializer

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

public class Customer {
    private int id;

    @JsonDeserialize(using = TrimmedStringDeserializer.class)
    private String name;

    public Customer() {
    }

    public Customer(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

If the incoming JSON contains "name": " Priya ", the custom deserializer trims the surrounding whitespace before assigning the value. In a production project, also decide how to handle blank strings, missing fields, and explicit null values.

21. Working with JsonNode for Dynamic JSON

Sometimes the structure of incoming JSON is not known in advance. Jackson's JsonNode tree model allows code to inspect JSON properties without first defining a complete Java class.

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonTreeExample {
    public static void main(String[] args) throws Exception {
        String json =
            "{\"id\":101,\"name\":\"Rahul\","
            + "\"address\":{\"city\":\"Mumbai\"}}";

        ObjectMapper mapper = new ObjectMapper();
        JsonNode root = mapper.readTree(json);

        int id = root.path("id").asInt();
        String name = root.path("name").asText();
        String city = root.path("address")
                          .path("city")
                          .asText();

        System.out.println(id);
        System.out.println(name);
        System.out.println(city);
    }
}

Using path() avoids a null result when a property is missing, returning a missing-node representation instead. However, applications should still validate whether required fields exist and whether their types are correct.

22. Handling JSON Errors and Exceptions

JSON processing can fail when input is malformed, a value has the wrong type, a required constructor property is missing, or the target Java class cannot be created. A REST API should handle these failures consistently.

22.1 Common Problems

Problem Example Recommended Action
Malformed JSON Missing quotation marks or braces Return a clear client error without exposing internal details.
Type mismatch A string is supplied for a numeric property Validate input and define consistent conversion rules.
Unknown property JSON contains an unexpected field Choose an intentional policy for strict or tolerant input.
Missing required value Required name is absent Use validation and suitable DTO constraints.
Unsupported Java type Mapper lacks the required module Register the correct module or use a supported type.
Unexpected date format Input date differs from the API contract Document and validate the expected format.

22.2 Handling Invalid Request Bodies in Spring Boot

Spring MVC commonly raises an HttpMessageNotReadableException when it cannot read or convert a request body. A global exception handler can return a controlled error response.

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class ApiExceptionHandler {

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<String> handleInvalidJson(
            HttpMessageNotReadableException exception) {

        return ResponseEntity
            .status(HttpStatus.BAD_REQUEST)
            .body("Invalid or unreadable JSON request body.");
    }
}

This example returns a simple text error response. A production API may instead return a structured error DTO containing an error code, a safe message, and a request identifier. Avoid returning raw exception messages, stack traces, or internal implementation details to clients.

23. Validating Incoming JSON

Successful deserialization does not mean that incoming data is valid. For example, a name can be deserialized correctly while still being blank. Bean Validation can enforce rules after JSON has been converted into a Java object.

23.1 Creating a Validated Request DTO

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Positive;

public class StudentRequest {

    @Positive
    private int id;

    @NotBlank
    private String name;

    @NotBlank
    @Email
    private String email;

    public StudentRequest() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

23.2 Using @Valid in a Controller

import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StudentApiController {

    @PostMapping("/api/students")
    public ResponseEntity<StudentRequest> createStudent(
            @Valid @RequestBody StudentRequest request) {

        return ResponseEntity.ok(request);
    }
}

Add the validation starter to the project when required. For example, Spring Boot applications commonly use spring-boot-starter-validation. Validation errors should be handled with a consistent response structure.

24. Security Best Practices for JSON Processing

  • Validate incoming data: Check required fields, lengths, ranges, formats, and business rules.
  • Use DTOs: Avoid binding external JSON directly to sensitive persistence entities.
  • Protect confidential values: Do not return passwords, authentication tokens, private keys, or internal secrets in JSON responses.
  • Set request size limits: Prevent oversized request bodies from consuming excessive memory or processing time.
  • Handle parser errors safely: Return controlled client-facing errors and keep diagnostic details in protected logs.
  • Review polymorphic deserialization: Avoid unsafe type-handling configurations for untrusted input.
  • Control unknown properties intentionally: Decide whether unexpected fields should be rejected or ignored according to the API contract.
  • Use HTTPS: Protect JSON data while it travels between clients and servers.
  • Apply authorization: Valid JSON does not prove that a client is allowed to access or modify a resource.
  • Test edge cases: Include nulls, missing fields, incorrect types, very long strings, and unexpected nested structures.

25. Testing JSON Serialization and Deserialization

Testing helps confirm that Java objects produce the expected JSON and that incoming JSON maps correctly to Java objects.

25.1 Unit Test for Serialization

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertTrue;

class StudentJsonTest {

    @Test
    void shouldSerializeStudent() throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        Student student = new Student(
            101,
            "Rahul",
            "[email protected]"
        );

        String json = mapper.writeValueAsString(student);

        assertTrue(json.contains("\"id\":101"));
        assertTrue(json.contains("\"name\":\"Rahul\""));
    }
}

25.2 Unit Test for Deserialization

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class StudentDeserializationTest {

    @Test
    void shouldDeserializeStudent() throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        String json =
            "{\"id\":101,\"name\":\"Rahul\","
            + "\"email\":\"[email protected]\"}";

        Student student = mapper.readValue(
            json,
            Student.class
        );

        assertEquals(101, student.getId());
        assertEquals("Rahul", student.getName());
        assertEquals("[email protected]", student.getEmail());
    }
}

For more robust tests, compare parsed JSON structures rather than relying on property ordering or exact whitespace. Also test malformed JSON, null handling, unknown fields, date formats, and custom serializers.

26. Testing REST APIs with Postman or curl

26.1 Test a GET Endpoint

Send a GET request to:

http://localhost:8080/students/sample

Confirm that the response body contains the expected JSON and that the HTTP status code is appropriate.

26.2 Test a POST Endpoint

Send a POST request to:

http://localhost:8080/students

Set the request header to Content-Type: application/json and provide the following body:

{
  "id": 103,
  "name": "Amit",
  "email": "[email protected]"
}

Example using curl:

curl -X POST "http://localhost:8080/students" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"id":103,"name":"Amit","email":"[email protected]"}'

Check the status code, response body, and error behavior for invalid inputs. If the endpoint returns the submitted object, remember that the demonstration controller is only echoing data rather than saving it to a database.

27. Common Mistakes and Troubleshooting

Mistake Why It Happens Solution
Invalid JSON syntax Missing commas, quotes, or braces Validate the JSON body before sending it.
Wrong Content-Type The request does not identify its body as JSON Use Content-Type: application/json.
Missing setters or compatible constructors The target class is not configured for the selected mapping approach Review constructors, getters, setters, annotations, and mapper configuration.
Unexpected property names JSON keys differ from Java property names Use consistent names or @JsonProperty.
Infinite recursion Bidirectional object relationships refer back to each other Use DTOs or configure relationship serialization carefully.
Sensitive data leakage Internal entity fields are serialized unintentionally Return dedicated response DTOs and review output fields.
Date parsing errors The input format or Java time module is incorrect Configure date-time support and document the expected format.
Unexpected null fields Input omitted fields or supplied explicit null values Validate required data and define null-handling rules.
Large JSON payloads Requests exceed practical resource limits Apply size limits and use streaming where appropriate.

28. Best Practices for JSON Serialization and Deserialization

  • Use the JSON mapper and dependency versions managed by the project's framework.
  • Prefer automatic Spring Boot JSON conversion for normal REST controller requests and responses.
  • Use explicit DTOs to define the public API contract.
  • Keep serialization rules predictable and documented.
  • Use BigDecimal for precise decimal arithmetic where appropriate, particularly for monetary values.
  • Use suitable Java time types and consistent date-time formats.
  • Use custom serializers and deserializers only when default mapping is insufficient.
  • Configure unknown-property handling based on compatibility and validation requirements.
  • Do not create a new mapper for every request; use Spring's configured mapper or a suitably managed mapper instance.
  • Write unit and integration tests for successful and unsuccessful cases.
  • Do not expose stack traces or sensitive internal values in API errors.
  • Keep API responses stable when changing Java model classes.

29. Practical Exercise

Exercise: Build a Student Management REST API that accepts and returns JSON data.

Requirements

  1. Create a Student model or request DTO with ID, name, email, and joining date.
  2. Implement a GET endpoint that returns a student as JSON.
  3. Implement a POST endpoint that accepts a JSON student object.
  4. Serialize a list of students into a JSON array.
  5. Deserialize a JSON array into a Java collection using TypeReference.
  6. Use @JsonProperty to customize at least one JSON field name.
  7. Use validation annotations to reject invalid student data.
  8. Return a controlled error response when the JSON request body is malformed.
  9. Write unit tests for serialization and deserialization.
  10. Test both valid and invalid requests with Postman or curl.

Expected Result

The application should convert Java objects to JSON responses, read JSON request bodies into Java objects, validate incoming data, and handle common conversion errors without exposing internal details.

30. Interview Questions and Answers

Q1. What is JSON serialization?

Answer: Serialization is the process of converting a Java object into JSON or another representation suitable for transmission or storage.

Q2. What is JSON deserialization?

Answer: Deserialization converts JSON data into a Java object that the application can process.

Q3. What is Jackson?

Answer: Jackson is a Java data-processing library that supports JSON parsing, generation, and object mapping. Spring Boot integrates Jackson when the appropriate dependencies and configuration are present.

Q4. What is ObjectMapper?

Answer: In Jackson 2, ObjectMapper is a central API for converting Java objects to JSON and JSON into Java objects.

Q5. What does writeValueAsString() do?

Answer: It serializes a Java value into a JSON string.

Q6. What does readValue() do?

Answer: It reads JSON input and converts it into a specified Java type.

Q7. How does Spring Boot automatically handle JSON?

Answer: Spring MVC uses configured HTTP message converters to deserialize request bodies and serialize response bodies when the appropriate JSON mapper is available.

Q8. What is the purpose of @RequestBody?

Answer: It instructs Spring MVC to read the HTTP request body and convert it into the declared Java type.

Q9. What is the purpose of @JsonProperty?

Answer: It customizes the JSON property name used for a Java property or constructor parameter.

Q10. What does @JsonIgnore do?

Answer: It excludes a property from normal Jackson serialization and deserialization behavior, subject to mapper configuration and access rules.

Q11. How do you serialize a list of Java objects?

Answer: Pass the list to the mapper's serialization method. Jackson serializes the collection as a JSON array.

Q12. How do you deserialize a JSON array into a List of objects?

Answer: Use readValue() with a TypeReference<List<Student>> so Jackson knows the element type.

Q13. Why use a custom serializer?

Answer: A custom serializer is useful when a value needs a specific output representation that default mapping cannot provide.

Q14. How should malformed JSON be handled in Spring Boot?

Answer: Handle request-body conversion exceptions, return a controlled client error, and avoid exposing stack traces or sensitive internal details.

Q15. Why should request DTOs be used?

Answer: DTOs define the accepted input and returned output, reduce accidental exposure of internal fields, and help separate API contracts from persistence models.

Q16. Does successful deserialization guarantee valid input?

Answer: No. Data may be syntactically valid but still violate required-field, format, range, authorization, or business rules. Validation is still necessary.

Q17. What is JsonNode?

Answer: JsonNode represents JSON as a tree, allowing an application to inspect dynamic JSON structures without first mapping them to a complete Java class.

Q18. How can null properties be omitted from JSON?

Answer: Use @JsonInclude(JsonInclude.Include.NON_NULL) or an appropriate mapper-level inclusion setting.

31. Quick Revision Checklist

  • Serialization converts Java objects to JSON.
  • Deserialization converts JSON to Java objects.
  • Jackson is a commonly used JSON mapping library in Spring Boot applications.
  • Jackson 2 commonly uses ObjectMapper; current Jackson 3 configurations use the JsonMapper API.
  • writeValueAsString() creates a JSON string.
  • readValue() converts JSON input into a target type.
  • @RequestBody supports request-body conversion.
  • @RestController supports response-body serialization.
  • @JsonProperty customizes property names.
  • @JsonIgnore excludes properties from normal mapping.
  • @JsonInclude controls property inclusion.
  • TypeReference helps deserialize generic collections.
  • Custom serializers and deserializers support specialized mapping requirements.
  • Validation and exception handling are essential for reliable APIs.
  • DTOs, secure error responses, and automated tests improve application quality.

32. Conclusion

JSON serialization and deserialization are essential skills for building Java applications that communicate through REST APIs. Serialization transforms Java objects into JSON responses, while deserialization converts incoming JSON into objects that application code can validate and process.

With Jackson and Spring Boot, much of this conversion can happen automatically through HTTP message converters. Understanding mapper APIs, annotations, nested objects, collections, date-time formats, custom mapping, validation, and exception handling makes it easier to build reliable and maintainable APIs.

To learn more about Java, Spring, and backend development, explore the Advanced Java Training Course, visit the JustAcademy website, or register for a course demo.

Previous Next
whatsapp