Of course! Here is a comprehensive guide to using JSON with Java, focusing on the most popular library: Jackson.

We'll cover:
- What is JSON and why use it?
- What is Jackson?
- Setting up Jackson in your project (Maven/Gradle).
- Core Jackson Concepts:
ObjectMapper,JsonParser,JsonGenerator. - Serialization: Converting Java objects to JSON.
- Deserialization: Converting JSON to Java objects.
- Handling Complex Scenarios:
- Polymorphic types (e.g.,
List,Map) - Customizing with Annotations (
@JsonProperty,@JsonIgnore, etc.) - Handling Date/Time objects
- Pretty-printing JSON
- Polymorphic types (e.g.,
- Best Practices and Common Pitfalls.
What is JSON and Why Use It?
JSON (JavaScript Object Notation) is a lightweight, text-based data-interchange format. It's easy for humans to read and write, and easy for machines to parse and generate. It's the de-facto standard for web APIs and configuration files.
Why use it in Java?
- Web APIs: Most modern REST APIs communicate by sending and receiving JSON data.
- Configuration: JSON is a human-readable alternative to XML or
.propertiesfiles. - Data Storage: Storing application data in a structured format.
What is Jackson?
Jackson is a high-performance, widely used Java library for processing JSON. It's the default JSON processor in the Spring Framework and many other popular Java libraries.

Key Features:
- High Performance: It's extremely fast and has a low memory footprint.
- Rich Feature Set: Supports a huge range of JSON features.
- Easy to Use: Provides a simple API for common tasks.
- Excellent Documentation: Well-maintained and comprehensive.
Setting up Jackson
First, you need to add the Jackson dependencies to your project. You'll almost always need the core, databind, and annotations modules.
Using Maven (pom.xml)
<dependencies>
<!-- The core databind module (includes core and annotations) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version> <!-- Use the latest version -->
</dependency>
</dependencies>
Using Gradle (build.gradle)
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0' // Use the latest version
Core Jackson Concepts
The central class in Jackson is ObjectMapper. It's the heart of the library and provides the main methods for reading and writing JSON.
import com.fasterxml.jackson.databind.ObjectMapper; // Create a single, shared instance for your application ObjectMapper objectMapper = new ObjectMapper();
Serialization: Java Object to JSON
Serialization is the process of converting a Java object into a JSON string.

Let's start with a simple POJO (Plain Old Java Object).
Example POJO: User.java
import java.util.Date;
public class User {
private String name;
private int age;
private String email;
private boolean isActive;
private Date registrationDate;
// Default constructor is required for deserialization
public User() {}
// Getters and Setters
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 String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public boolean isActive() { return isActive; }
public void setActive(boolean active) { isActive = active; }
public Date getRegistrationDate() { return registrationDate; }
public void setRegistrationDate(Date registrationDate) { this.registrationDate = registrationDate; }
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
", isActive=" + isActive +
", registrationDate=" + registrationDate +
'}';
}
}
Serialization Code
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Date;
public class SerializationExample {
public static void main(String[] args) throws Exception {
// 1. Create an instance of ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
// 2. Create a Java object
User user = new User();
user.setName("John Doe");
user.setAge(30);
user.setEmail("john.doe@example.com");
user.setActive(true);
user.setRegistrationDate(new Date());
// 3. Serialize the object to a JSON string
// The writeValueAsString() method is the most common way.
String jsonString = objectMapper.writeValueAsString(user);
// 4. Print the result
System.out.println(jsonString);
}
}
Output:
{"name":"John Doe","age":30,"email":"john.doe@example.com","isActive":true,"registrationDate":1703123456789}
Deserialization: JSON to Java Object
Deserialization is the reverse process: converting a JSON string into a Java object.
Deserialization Code
import com.fasterxml.jackson.databind.ObjectMapper;
public class DeserializationExample {
public static void main(String[] args) throws Exception {
// 1. Create an instance of ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
// 2. JSON string to be converted
String jsonString = "{\"name\":\"Jane Doe\",\"age\":28,\"email\":\"jane.doe@example.com\",\"isActive\":false,\"registrationDate\":1703123456789}";
// 3. Deserialize the JSON string into a User object
// The readValue() method requires the JSON string and the target class type.
User user = objectMapper.readValue(jsonString, User.class);
// 4. Print the Java object
System.out.println(user);
}
}
Output:
User{name='Jane Doe', age=28, email='jane.doe@example.com', isActive=false, registrationDate=Wed Dec 20 10:30:56 UTC 2025}
Handling Complex Scenarios
A. Polymorphic Types (Lists, Maps)
ObjectMapper can handle collections and maps directly.
Example: Serializing a List<User>
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Arrays;
import java.util.List;
public class ListSerializationExample {
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
User user1 = new User(); // ... set properties
user1.setName("Alice");
user1.setAge(25);
User user2 = new User(); // ... set properties
user2.setName("Bob");
user2.setAge(40);
List<User> userList = Arrays.asList(user1, user2);
// Serialize the list directly
String jsonString = objectMapper.writeValueAsString(userList);
System.out.println(jsonString);
}
}
Output:
[{"name":"Alice","age":25,"email":null,"isActive":false,"registrationDate":null},{"name":"Bob","age":40,"email":null,"isActive":false,"registrationDate":null}]
Example: Deserializing into a List<User>
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
public class ListDeserializationExample {
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
String jsonArrayString = "[{\"name\":\"Alice\",\"age\":25},{\"name\":\"Bob\",\"age\":40}]";
// Use TypeReference to specify the generic type
List<User> userList = objectMapper.readValue(jsonArrayString, new TypeReference<List<User>>() {});
userList.forEach(System.out::println);
}
}
Note: When deserializing into a generic type like List<T> or Map<K, V>, you must use objectMapper.readValue(jsonString, new TypeReference<YourGenericType>() {}); because Java type erasure prevents objectMapper.readValue(jsonString, List.class) from working correctly.
B. Customizing with Annotations
Jackson provides powerful annotations to control the serialization/deserialization process.
| Annotation | Description |
|---|---|
@JsonProperty("json_name") |
Maps a field to a specific JSON property name. |
@JsonIgnore |
Excludes a field from being serialized or deserialized. |
@JsonIgnoreProperties(ignoreUnknown = true) |
On a class, tells Jackson to ignore any properties in the JSON that don't have a corresponding field in the Java class. |
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") |
Formats a date/time field into a specific string pattern. |
Example: User.java with Annotations
import com.fasterxml.jackson.annotation.*;
import java.util.Date;
@JsonInclude(JsonInclude.Include.NON_NULL) // Don't include null values in JSON
@JsonIgnoreProperties(ignoreUnknown = true) // Ignore unknown JSON properties
public class User {
@JsonProperty("full_name")
private String name;
private int age;
@JsonIgnore // Never serialize/deserialize this field
private String internalId;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss")
private Date registrationDate;
// Getters and Setters...
}
C. Pretty-Printing JSON
To make the output human-readable (e.g., for debugging), enable pretty-printing.
import com.fasterxml.jackson.databind.ObjectMapper;
public class PrettyPrintExample {
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
// Enable pretty printing
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
User user = new User();
user.setName("John Doe");
user.setAge(30);
String jsonString = objectMapper.writeValueAsString(user);
System.out.println(jsonString);
}
}
Output:
{
"name" : "John Doe",
"age" : 30,
"email" : null,
"isActive" : false,
"registrationDate" : null
}
Best Practices and Common Pitfalls
-
Reuse
ObjectMapper: Creating anObjectMapperis an expensive operation. Create one instance per application (or per thread if necessary) and reuse it. It's thread-safe for read operations. -
Handle Exceptions: Jackson operations (
writeValueAsString,readValue) throwIOException. Always wrap them in atry-catchblock.try { String json = objectMapper.writeValueAsString(myObject); } catch (JsonProcessingException e) { // Handle the exception, e.g., log it e.printStackTrace(); } -
Use
@JsonIgnoreProperties: When consuming external APIs, you can't control the JSON schema. Use@JsonIgnoreProperties(ignoreUnknown = true)on your POJO to prevent your application from crashing if the JSON contains extra fields. -
Immutable Objects: For better thread safety and design, you might use immutable objects (with
finalfields). Jackson can handle these using a constructor or a static "factory method". Use@JsonCreatorand@JsonPropertyon the constructor/factory method.public final class ImmutableUser { private final String name; private final int age; @JsonCreator // Marks this method as the factory for deserialization public ImmutableUser(@JsonProperty("name") String name, @JsonProperty("age") int age) { this.name = name; this.age = age; } // Getters... } -
Circular References: If two objects reference each other (e.g.,
Userhas aList<Post>and eachPosthas aUserauthor), Jackson will run into an infinite loop and throw aStackOverflowError. To fix this, use@JsonIgnoreon one side of the relationship or use@JsonManagedReferenceand@JsonBackReference.
