Why Do Jackson 2 and 3 Coexist? Uncover the 4 Breaking Changes in Jackson 3
After upgrading to Spring Boot 4, the article explains why both Jackson 2 and Jackson 3 appear together, outlines the four major breaking changes—including package reorganization, the shift from ObjectMapper to JsonMapper, new default date serialization, and the removal of checked exceptions—and offers migration tips.
Why Jackson 2 and 3 appear together?
After upgrading to Spring Boot 4, the dependency tree shows both Jackson 2 and Jackson 3 artifacts:
spring-boot-starter-jackson (4.x)
├── tools.jackson.core:jackson-core:3.x ← core implementation (new package)
└── com.fasterxml.jackson.annotation:jackson-annotations:2.x ← annotation classes (old package)The Jackson team kept the annotation package for backward compatibility while moving the core implementation to a new namespace.
Jackson 3’s four major breaking changes
1. Package reorganization
All core classes moved from com.fasterxml to tools.jackson. Example imports:
// Jackson 2 (old)
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
// Jackson 3 (new)
import tools.jackson.core.JacksonException;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.ObjectMapper;Only the core API changed packages; annotation classes remain under the old namespace. A blind global replace of com.fasterxml will break annotations.
2. ObjectMapper replaced by JsonMapper
Jackson 3 enforces an immutable builder pattern. The recommended way to create a mapper is:
JsonMapper mapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.build();Calling build() locks the configuration, making the mapper thread‑safe. Directly instantiating new ObjectMapper() yields default settings and cannot be customized without the builder.
3. Date‑time serialization defaults
Jackson 3 now serializes dates as ISO‑8601 strings instead of numeric timestamps. Example:
Jackson 2 default : {"now":1767588151648} (timestamp)
Jackson 3 default : {"now":"2026-01-05T02:02:31Z"} (ISO‑8601 string)
If client code expects a numeric timestamp, the upgrade will cause failures. A temporary workaround is to set use-jackson2-defaults: true in application.yml, but this is not recommended for long‑term use.
4. Exception handling – checked exceptions removed
All Jackson I/O methods now throw RuntimeException instead of checked IOException. This simplifies usage with streams and lambdas:
list.stream()
.map(o -> jsonMapper.writeValueAsString(o))
.toList();The design aligns Jackson with modern Java practices and removes the need for explicit try‑catch blocks around serialization calls.
Summary
When migrating to Jackson 3, be aware of the package rename, the shift to JsonMapper with an immutable builder, the change in default date format, and the removal of checked exceptions. Adjust imports, configure the mapper via the builder, update any timestamp‑expecting code, and remove unnecessary catch clauses to ensure a smooth upgrade.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
