> For the complete documentation index, see [llms.txt](https://www.pranaypourkar.co.in/the-programmers-guide/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.pranaypourkar.co.in/the-programmers-guide/spring/utilities-and-libraries/jackson-objectmapper.md).

# Jackson ObjectMapper

## About

`ObjectMapper` is a core class provided by the **Jackson** library, used for converting between Java objects and JSON. It allows us to:

* Serialize Java objects to JSON
* Deserialize JSON to Java objects
* Work with Maps, Lists, and generic types
* Configure inclusion/exclusion rules
* Register modules for special data types like Java 8 date/time, Java records, etc.

Although Jackson is not a Spring-native library, **Spring Boot auto-configures Jackson** and provides seamless integration out-of-the-box, making `ObjectMapper` one of the most commonly used utilities in Spring-based applications.

## **Dependency**

If we are using Spring Boot with web or REST modules, Jackson is included automatically via the `spring-boot-starter-web` dependency:

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

This transitively includes:

```xml
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
</dependency>
```

If we are not using Spring Boot or want to include Jackson manually, add:

```xml
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.17.0</version> <!-- Use latest stable -->
</dependency>
```

## **Spring Integration**

### **How Spring Boot Uses Jackson Internally ?**

Spring Boot uses Jackson as the default JSON processor. When we build a REST API or web application with `spring-boot-starter-web`, it:

* Automatically includes the Jackson library (`jackson-databind`)
* Registers a **singleton `ObjectMapper` bean** in the Spring context
* Uses that bean for:
  * Serializing controller response objects (`@ResponseBody`)
  * Deserializing request bodies into Java objects (`@RequestBody`)
  * JSON conversion in `RestTemplate`, `WebClient`, and Feign clients

We can inject and use this same ObjectMapper throughout our application:

```java
@Autowired
private ObjectMapper objectMapper;
```

### **Default Behavior in Spring Boot**

When using Spring Boot, the default `ObjectMapper` is configured with sensible defaults.

#### **Example Defaults**

<table data-full-width="true"><thead><tr><th width="328.5399169921875">Feature</th><th>Default Behavior</th></tr></thead><tbody><tr><td>Include <code>null</code> fields in JSON</td><td>Yes</td></tr><tr><td>Pretty-print JSON</td><td>No</td></tr><tr><td>Fail on unknown JSON properties</td><td>No (<code>FAIL_ON_UNKNOWN_PROPERTIES</code> is disabled)</td></tr><tr><td>Support for Java 8 Date/Time types</td><td>Yes (via <code>jackson-datatype-jsr310</code>)</td></tr><tr><td>Use of ISO-8601 for date serialization</td><td>Yes</td></tr><tr><td>Support for Java 8 Optional types</td><td>Yes</td></tr><tr><td>Snake case support</td><td>No (uses camelCase by default)</td></tr></tbody></table>

We can override these behaviors either:

1. Through application properties
2. By providing a custom `ObjectMapper` bean

### Customizing ObjectMapper via `application.yml`

```yaml
spring:
  jackson:
    # Include strategy for properties
    # Options: always, non_null, non_absent, non_default, non_empty
    default-property-inclusion: non_null  # Exclude null fields from JSON output

    # Serialization settings
    serialization:
      indent_output: true  # Pretty-print the JSON output (adds newlines and spaces)

    # Deserialization settings
    deserialization:
      fail-on-unknown-properties: false  # Do not fail if JSON has unknown fields

    # Date and time formatting
    date-format: yyyy-MM-dd'T'HH:mm:ss  # Custom global date-time format
    time-zone: Asia/Kolkata             # Time zone to use for date/time serialization

    # Naming strategy for property names
    # Options: SNAKE_CASE, LOWER_CAMEL_CASE (default), UPPER_CAMEL_CASE, KEBAB_CASE, etc.
    property-naming-strategy: SNAKE_CASE  # Converts Java fields like "firstName" to "first_name"

    # Enable/disable features (serialization/deserialization-level)
    mapper:
      # Allow use of getter methods as creators (if no constructor present)
      allow-getters-as-setters: true

    # Visibility control (used less frequently)
    visibility:
      # Property-level visibility configuration
      field: any                # Detect all fields regardless of access modifier
      getter: none              # Ignore getters
      is-getter: none           # Ignore "isX()" methods for booleans
      setter: any               # Detect all setters
      creator: any              # Detect constructors and factory methods
```

### Internally, Spring Boot does this

Spring Boot binds these properties into a `Jackson2ObjectMapperBuilder` and applies them when initializing the `ObjectMapper` bean.

This is equivalent to doing this programmatically:

```java
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"));
```

### Overriding the Default ObjectMapper

If we need full control, define our own `@Bean` of type `ObjectMapper`. Spring Boot will use our version instead of the default.

```java
@Configuration
public class JacksonConfig {
    
    @Bean
    public ObjectMapper customObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        mapper.registerModule(new JavaTimeModule());
        mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
        return mapper;
    }
}
```

> Note: If we do this, make sure to register required modules like `JavaTimeModule`, otherwise date/time parsing may break.

## Use Cases

### 1. **Serialization: Java Object to JSON**

Convert a Java object into a JSON string.

```java
Person person = new Person("John", 30);
String json = objectMapper.writeValueAsString(person);
```

Write to file or output stream:

```java
objectMapper.writeValue(new File("person.json"), person);
```

Pretty-printing:

```java
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
String prettyJson = objectMapper.writeValueAsString(person);
```

### 2. **Deserialization: JSON to Java Object**

Convert a JSON string to a Java object.

```java
String json = "{\"name\":\"John\",\"age\":30}";
Person person = objectMapper.readValue(json, Person.class);
```

From file:

```java
Person person = objectMapper.readValue(new File("person.json"), Person.class);
```

### 3. **Convert Between Java Objects (Object to Object Mapping)**

Convert one type to another using intermediate JSON conversion. Useful for mapping DTOs to entities.

```java
UserDTO dto = new UserDTO("john.doe", "Admin");
UserEntity entity = objectMapper.convertValue(dto, UserEntity.class);
```

### 4. Convert to and from Map

#### **POJO Class**

```java
public class User {
    private String name;
    private int age;

    // Constructors, getters, setters
}
```

#### **Convert POJO to Map**

```java
User user = new User("Alice", 25);
Map<String, Object> map = objectMapper.convertValue(user, new TypeReference<>() {});
System.out.println(map);
```

```
{name=Alice, age=25}
```

#### **Convert Map to POJO**

```java
<String, Object> map = new HashMap<>();
map.put("name", "Bob");
map.put("age", 30);

User user = objectMapper.convertValue(map, User.class);
System.out.println(user.getName());  // Bob
System.out.println(user.getAge());   // 30
```

### 5. **Handling Collections and Generics**

Deserialize a JSON array into a list of objects:

```java
String json = "[{\"name\":\"John\",\"age\":30},{\"name\":\"Jane\",\"age\":25}]";

List<Person> people = objectMapper.readValue(
    json, new TypeReference<List<Person>>() {}
);
```

Deserialize a nested structure:

```java
String json = "{\"group\":\"team1\",\"members\":[{\"name\":\"John\"},{\"name\":\"Jane\"}]}";

Group group = objectMapper.readValue(json, Group.class);
```

### **6. Deserialization with Unknown or Partial JSON**

#### **POJO Class**

```java
public class Product {
    private String name;
    private double price;

    // Constructors, getters, setters
}
```

#### **JSON with Extra Field (unknown)**

```java
String json = """
{
    "name": "Laptop",
    "price": 999.99,
    "warranty": "2 years"  // Not present in Product class
}
""";
```

#### **Default Behavior (in Spring Boot)**

By default, Spring Boot configures `ObjectMapper` to **ignore unknown fields**, so this will **work fine**.

```java
Product product = objectMapper.readValue(json, Product.class);
System.out.println(product.getName());   // Laptop
System.out.println(product.getPrice());  // 999.99
```

#### **Manually Ensuring it Ignores Unknown Fields**

If we are not in Spring Boot, or just want to be sure:

```java
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
```

#### **Partial JSON**

```java
String partialJson = """
{
    "name": "Phone"
}
""";
```

If `price` is missing, it will be set to its default value (e.g., `0.0` for `double`).

```java
Product product = objectMapper.readValue(partialJson, Product.class);
System.out.println(product.getName());   // Phone
System.out.println(product.getPrice());  // 0.0
```

## **Serialization Inclusion Strategies**

Serialization inclusion strategies determine **what data gets included** in the JSON output. This is important to reduce unnecessary data in API responses, especially when dealing with large objects or REST services.

#### **Options**

<table><thead><tr><th width="271.287353515625">Strategy</th><th>Description</th></tr></thead><tbody><tr><td><code>Include.ALWAYS</code></td><td>Always include the property (default)</td></tr><tr><td><code>Include.NON_NULL</code></td><td>Exclude <code>null</code> values</td></tr><tr><td><code>Include.NON_EMPTY</code></td><td>Exclude empty values (empty string, empty list, etc.)</td></tr><tr><td><code>Include.NON_DEFAULT</code></td><td>Exclude fields with default values</td></tr><tr><td><code>Include.NON_ABSENT</code></td><td>Exclude nulls and Java 8 <code>Optional.empty()</code></td></tr></tbody></table>

#### **Global Configuration**

```java
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
```

#### **Field-Level Configuration**

```java
public class User {
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private List<String> tags;
}
```

#### **Spring Boot Configuration (application.yml)**

```yaml
spring:
  jackson:
    default-property-inclusion: non_null
```

#### **Use Case Example**

You may not want to return null or empty fields to reduce response payload size:

```json
{
  "name": "John",
  "email": null
}
```

With `NON_NULL`, `email` will be excluded.

## **Date and Time Handling**

Working with dates and times in Java can be tricky, especially when serializing and deserializing JSON in REST APIs. Jackson provides powerful capabilities for formatting and parsing date/time types, and Spring Boot makes this integration seamless—but only if you configure it correctly.

#### **Why Special Handling Is Needed**

Java 8 introduced a new Date-Time API under `java.time.*` packages like:

* `LocalDate`
* `LocalDateTime`
* `ZonedDateTime`
* `Instant`

These types are **not supported natively** by Jackson without additional configuration. Jackson treats them differently than older types like `java.util.Date`, and without proper setup, they are either:

* Serialized as **arrays** (e.g. `[2024, 6, 20]`)
* Serialized as **timestamps**
* Or cause **deserialization errors**

#### **Solution**

To handle Java 8 time types correctly:

1. **Register `JavaTimeModule`**
2. **Disable writing as timestamps**

```java
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
```

This enables human-readable ISO-8601 strings like:

```json
"2024-06-20T15:45:00"
```

instead of:

```json
[2024, 6, 20, 15, 45, 0]
```

#### **Field-Level Formatting**

Use `@JsonFormat` to define a custom date format for individual fields:

```java
public class Event {
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate date;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime dateTime;
}
```

This ensures consistent formatting even if the global `ObjectMapper` is shared across the application.

#### **Global Formatting in Spring Boot**

Spring Boot allows configuring date/time formats centrally using `application.yml` or `application.properties`.

**YAML Configuration**

```yaml
spring:
  jackson:
    date-format: yyyy-MM-dd'T'HH:mm:ss
    time-zone: Asia/Kolkata
```

**Effect**

This:

* Sets the format for all `java.util.Date` and `Calendar`
* Does **not** apply to Java 8 `java.time.*` types unless you register `JavaTimeModule`

> To apply it for both, combine Spring config + `JavaTimeModule` registration.

#### **Timezone Handling**

When serializing or deserializing time-zone-aware types like `ZonedDateTime` or `OffsetDateTime`, Jackson can:

* Use default system time zone
* Apply a specific time zone via config
* Respect explicit time zone in the string

**Set Global Time Zone**

```java
mapper.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
```

Or via Spring config:

```yaml
spring:
  jackson:
    time-zone: Asia/Kolkata
```

#### **Deserialization Considerations**

When parsing strings, Jackson expects them to match the format defined by:

* `@JsonFormat` (if present)
* `application.yml` format (if configured)
* ISO-8601 (default fallback for `JavaTimeModule`)

Mismatched formats will cause `JsonParseException`.

## **Field Naming Strategy**

Field naming strategies control how Java field names are mapped to JSON keys.

#### **Default: camelCase**

```java
firstName → "firstName"
```

#### **snake\_case:**

```java
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
```

```java
firstName → "first_name"
```

#### **Spring Boot Configuration**

```yaml
spring:
  jackson:
    property-naming-strategy: SNAKE_CASE
```

#### **Use Case**

Aligns backend field names with frontend naming conventions, especially in REST APIs.

## **Working with JSON Tree (JsonNode)**

Use `JsonNode` when working with **dynamic, partially known, or schema-less** JSON structures.

#### **Parsing JSON as Tree**

```java
String json = "{\"name\":\"Alice\", \"age\":30}";
JsonNode root = objectMapper.readTree(json);
String name = root.get("name").asText();     // "Alice"
int age = root.get("age").asInt();           // 30
```

#### **Modifying Tree**

```java
((ObjectNode) root).put("name", "Bob");
```

#### **Traversing Nested Nodes**

```java
JsonNode address = root.path("address");
String city = address.path("city").asText();
```

#### **Creating a Tree Manually**

```java
ObjectNode node = objectMapper.createObjectNode();
node.put("username", "john");
node.put("active", true);
```

#### **Use Case**

Perfect for generic filters, form submissions, or when schema is not static.

## **Annotations for Fine-Grained Control**

Jackson provides annotations to control serialization/deserialization behavior per class or field.

#### **Common Annotations**

<table><thead><tr><th width="225.60589599609375">Annotation</th><th>Purpose</th></tr></thead><tbody><tr><td><code>@JsonProperty</code></td><td>Rename JSON property</td></tr><tr><td><code>@JsonIgnore</code></td><td>Exclude field</td></tr><tr><td><code>@JsonInclude</code></td><td>Conditional inclusion</td></tr><tr><td><code>@JsonFormat</code></td><td>Format dates</td></tr><tr><td><code>@JsonAlias</code></td><td>Accept multiple names</td></tr><tr><td><code>@JsonCreator</code></td><td>Constructor/factory-based deserialization</td></tr><tr><td><code>@JsonValue</code></td><td>Serialize enum or custom object as a value</td></tr></tbody></table>

#### **Examples**

```java
public class Product {
    @JsonProperty("item_name")
    private String name;

    @JsonIgnore
    private String internalCode;

    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate releaseDate;

    @JsonAlias({"cost", "price_value"})
    private double price;
}
```

#### **Use Case**

* Maintain compatibility with legacy JSON.
* Adjust naming or formatting without changing the class model.
* Control visibility per API contract.

## **Working with JSON Tree (JsonNode)**

Use `JsonNode` when working with **dynamic, partially known, or schema-less** JSON structures.

#### **Parsing JSON as Tree**

```java
String json = "{\"name\":\"Alice\", \"age\":30}";
JsonNode root = objectMapper.readTree(json);
String name = root.get("name").asText();     // "Alice"
int age = root.get("age").asInt();           // 30
```

#### **Modifying Tree**

```java
((ObjectNode) root).put("name", "Bob");
```

#### **Traversing Nested Nodes**

```java
JsonNode address = root.path("address");
String city = address.path("city").asText();
```

#### **Creating a Tree Manually**

```java
ObjectNode node = objectMapper.createObjectNode();
node.put("username", "john");
node.put("active", true);
```

#### **Use Case**

Perfect for generic filters, form submissions, or when schema is not static.

## **Exception Handling**

Jackson can throw several checked and runtime exceptions during JSON processing. It’s essential to handle these gracefully, especially in REST APIs.

#### **Common Exceptions**

<table><thead><tr><th width="292.55035400390625">Exception</th><th>Cause</th></tr></thead><tbody><tr><td><code>UnrecognizedPropertyException</code></td><td>JSON has fields not found in the POJO</td></tr><tr><td><code>MismatchedInputException</code></td><td>Type mismatch (e.g., expecting object but got array)</td></tr><tr><td><code>JsonMappingException</code></td><td>Problems mapping between JSON and object</td></tr><tr><td><code>JsonParseException</code></td><td>Malformed JSON syntax</td></tr><tr><td><code>JsonProcessingException</code></td><td>Parent class for all exceptions</td></tr></tbody></table>

#### **Spring REST Exception Handler**

```java
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(JsonProcessingException.class)
    public ResponseEntity<String> handleJsonException(JsonProcessingException e) {
        return ResponseEntity.badRequest().body("Invalid JSON: " + e.getOriginalMessage());
    }
}
```

## **Custom Serializers and Deserializers**

In most applications, Jackson’s default serialization and deserialization behavior is sufficient. However, certain situations call for **custom logic**, such as:

* Encrypting/masking sensitive data
* Transforming legacy formats
* Applying business rules during serialization or deserialization
* Interacting with non-standard or inconsistent APIs

Jackson makes this easy with `JsonSerializer<T>` and `JsonDeserializer<T>` interfaces.

### **1. Custom Serializer**

#### **Goal:** Control how a Java object or field is written to JSON.

#### **Steps:**

**a) Create a serializer class:**

```java
public class MaskingSerializer extends JsonSerializer<String> {
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers)
            throws IOException {
        gen.writeString("***"); // Replace actual value with ***
    }
}
```

**b) Use it in our model:**

```java
public class User {
    private String name;

    @JsonSerialize(using = MaskingSerializer.class)
    private String ssn; // Sensitive data
}
```

**Output:**

```json
{
  "name": "John",
  "ssn": "***"
}
```

### **2. Custom Deserializer**

#### **Goal:** Control how JSON is parsed into a Java object or field.

#### **Steps:**

**a) Create a deserializer class:**

```java
public class UppercaseDeserializer extends JsonDeserializer<String> {
    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException {
        return p.getText().toUpperCase();
    }
}
```

**b) Use it in our model:**

```java
public class Product {
    private String code;

    @JsonDeserialize(using = UppercaseDeserializer.class)
    private String category;
}
```

**Input:**

```json
{
  "code": "PRD-101",
  "category": "electronics"
}
```

**Output:**

```java
product.getCategory(); // "ELECTRONICS"
```

### **3. Apply to Entire Class**

We can serialize/deserialize an entire class with custom logic if we need control beyond a field level.

#### **Example: Serialize full object to flat string**

```java
public class Coordinates {
    public double lat;
    public double lng;
}
```

**Custom Serializer:**

```java
public class CoordinatesSerializer extends JsonSerializer<Coordinates> {
    @Override
    public void serialize(Coordinates value, JsonGenerator gen, SerializerProvider serializers)
            throws IOException {
        gen.writeString(value.lat + "," + value.lng);
    }
}
```

**Use:**

```java
@JsonSerialize(using = CoordinatesSerializer.class)
public class Coordinates { ... }
```

**Output:**

```json
"12.9716,77.5946"
```

### **4. Global Registration**

Instead of annotating each model field or class, we can register custom (de)serializers globally.

```java
@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();

        SimpleModule module = new SimpleModule();
        module.addSerializer(String.class, new MaskingSerializer());
        module.addDeserializer(String.class, new UppercaseDeserializer());

        mapper.registerModule(module);
        return mapper;
    }
}
```

> This affects **every** `String` field across the application. Be cautious with global registration.

### **5. Use with Spring Boot's Default ObjectMapper**

If we don’t want to override Spring Boot’s default `ObjectMapper` but want to **add custom behavior**, use a `Jackson2ObjectMapperBuilderCustomizer`:

```java
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
    return builder -> {
        SimpleModule module = new SimpleModule();
        module.addSerializer(String.class, new MaskingSerializer());
        builder.modules(module);
    };
}
```

This allows customization **without replacing** the default Spring Boot configuration.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.pranaypourkar.co.in/the-programmers-guide/spring/utilities-and-libraries/jackson-objectmapper.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
