Jackson 3 Breaks Everything: New Package, Immutable Mapper, and a Mandatory JDK 17 Floor
Foreword
Recently, while upgrading to Spring Boot 4, I discovered a very common phenomenon — most people are caught off guard by the 'invisible bomb' that is Jackson 3.
After the project started, the console reported a bunch of inexplicable errors.
Looking over them, it turned out not to be a problem with the business code, but breaking changes brought by the Jackson upgrade.
"Isn't Jackson just a JSON serialization tool? What's the big deal about upgrading?"
If you think that way, it means you haven't truly understood Jackson's position in the Java ecosystem.
Jackson is not just 'a JSON library'; it is the de facto standard JSON implementation in the Java ecosystem.
Spring Boot, microservice communication, Redis serialization, log parsing — almost everywhere JSON is involved, Jackson is used under the hood.
Upgrading from Jackson 2.x to 3.x is not a 'seamless upgrade'.
It is Jackson's first major version number change in 8 years.
From its inception at the end of 2017 to the official GA release on October 3, 2025, it went through 10 RC versions.
In this article today, I will break down Jackson 3's new features and migration points for you from start to finish.
I hope it will be helpful to you.
For more project practices, visit my technical website: susan.net.cn/project
1. What Exactly Is 'New' About Jackson 3?
Before discussing specific features, let's establish an overall understanding.
Jackson 3 is a refactoring-oriented evolution with a very clear set of values.
If Jackson 2 leaned more towards 'compatibility with all historical baggage', then Jackson 3's goal is very clear — security, type clarity, and modern Java orientation.
This diagram basically summarizes the core changes of Jackson 3.
Below, I will break them down for you one by one.
2. JDK Baseline Upgraded to Java 17
This is the most fundamental change in Jackson 3. Jackson 3.x requires JDK 17+.
Jackson 2.x minimally supports JDK 8.
From JDK 8 to JDK 17, 9 years and 4 LTS versions have passed.
This upgrade means Jackson 3 can fully utilize the new features of Java 17 — Records, Sealed Classes, Pattern Matching, etc.
Impact on you: If your project is still on JDK 8 or 11 and you want to use Jackson 3, you must upgrade the JDK first. The good news is that Spring Boot 3.x already requires JDK 17, so most new projects already meet this condition.
3. Comprehensive Changes to Package Names and groupId
This is the most 'conspicuous' change, and also the first compilation error you will encounter when upgrading.
| Comparison Item | Jackson 2.x | Jackson 3.x |
|---|---|---|
| Maven groupId | com.fasterxml.jackson |
tools.jackson |
| Java Package Name | com.fasterxml.jackson.xxx |
tools.jackson.xxx |
| Annotation Package Name | com.fasterxml.jackson.annotation |
Remains Unchanged |
// Jackson 2.x style
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonParser;
// Jackson 3.x style
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.core.JsonParser;
Why does the annotation package name remain unchanged?
This is a very clever design. The Jackson team left the annotation library (jackson-annotations) under the original groupId and package name, and only moved the core processing logic to tools.jackson. Jackson 3.0 uses jackson-annotations version 2.20.
This means: Jackson 2 and Jackson 3 can coexist in the same project. Your core application can use Jackson 3, while old third-party dependency libraries can still use Jackson 2, without interfering with each other.
But note one exception: Annotations inside jackson-databind (like @JsonSerialize, @JsonDeserialize) will move to the new package tools.jackson.databind.annotation.
4. ObjectMapper Becomes the Immutable JsonMapper
This is the change with the widest impact in Jackson 3.
In Jackson 2.x, ObjectMapper is mutable. You could write:
// Jackson 2.x - Mutable configuration
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT); // Can be changed anytime
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// State can be modified at any time
This design can cause problems in multi-threaded environments — one thread modifies the configuration while another is using it, leading to unpredictable behavior.
Jackson 3.x mandates the Builder pattern, and the configuration is locked once construction is complete:
// Jackson 3.x - Immutable Builder pattern
JsonMapper mapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.build(); // Configuration is locked once build() is called
Must use format-aligned subclasses: In Jackson 3, you must use an ObjectMapper subclass that matches the data format:
// Jackson 2.x - Can mix
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
// Jackson 3.x - Must use the corresponding Mapper
JsonMapper jsonMapper = JsonMapper.builder().build(); // JSON
YAMLMapper yamlMapper = YAMLMapper.builder().build(); // YAML
XmlMapper xmlMapper = XmlMapper.builder().build(); // XML
Prohibited: The new ObjectMapper(new YAMLFactory()) style.
5. Exception System Refactored
Jackson 2.x uses Checked Exceptions — JsonProcessingException is a subclass of Exception, requiring explicit try-catch or throws.
Jackson 3.x switches to Unchecked Exceptions:
| Jackson 2.x | Jackson 3.x |
|---|---|
JsonProcessingException |
JacksonException (Base class) |
JsonParseException |
StreamReadException |
JsonEOFException |
UnexpectedEndOfInputException |
// Jackson 2.x - Must handle checked exception
try {
User user = mapper.readValue(json, User.class);
} catch (JsonProcessingException e) {
// Must handle
}
// Jackson 3.x - Unchecked exception, can choose not to catch
User user = mapper.readValue(json, User.class);
// If an error occurs, throws JacksonException (runtime exception)
This change makes Jackson's API more aligned with modern Java programming habits, consistent with mainstream frameworks like Spring and Lombok.
6. Comprehensive Adjustment of Default Configuration
Jackson 3 has changed a large number of default configurations.
I'll pick a few that are most likely to 'blow up on you'.
6.1 Date Serialization: From Timestamps to ISO-8601 Strings
This is one of the most easily overlooked and most impactful changes.
// Jackson 2.x Default: 2026-07-20 12:00:00 → 1721462400000 (Timestamp)
// Jackson 3.x Default: 2026-07-20 12:00:00 → "2026-07-20T12:00:00Z" (ISO-8601 String)
If your frontend code depends on the timestamp format, the data format returned by the API will change after the upgrade. The good news is, it can be explicitly configured back:
JsonMapper mapper = JsonMapper.builder()
.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // Change back to timestamps
.build();
6.2 Primitive Type Null Values: From Lenient to Strict
Jackson 3.x changes the default value of FAIL_ON_NULL_FOR_PRIMITIVES from false to true.
// Suppose you have this DTO
public class User {
private int age; // Primitive type int
}
// Jackson 2.x: When age is null in JSON, defaults to 0
// Jackson 3.x: When age is null in JSON, throws JsonMappingException
This change will cause many codes that 'used to run' to suddenly error. Solution: either change int to Integer, or explicitly disable this feature.
6.3 Month Counting: From 0-based to 1-based
In Java's java.util.Date, months start from 0 (0=January). Jackson 3.x changes the default value of DateTimeFeature.ONE_BASED_MONTHS from false to true.
This means the handling of months during serialization/deserialization has changed. If your code relies on the '0=January' logic, pay special attention after upgrading.
7. Performance and Memory Optimization
Jackson 3 is not just about 'changing APIs'; it has also done a lot of optimization in performance and memory.
7.1 BeanDescription Lazy Loading Optimization
Jackson 3.0 introduces the Supplier pattern to implement lazy loading for the BeanDescription implementation. For simple types or scenarios not requiring a full BeanDescription, unnecessary parsing overhead is avoided. This reduces the creation of temporary objects and GC pressure.
7.2 RecyclerPool Lazy Initialization
Jackson Core 3.0 optimizes the initialization strategy for RecyclerPool. In applications with high-frequency Jackson usage, this can significantly improve memory usage efficiency.
7.3 Field Name Deduplication Disabled
Jackson 3.0 changes the default value of TokenStreamFactory.Feature.INTERN_FIELD_NAMES to false. This means JSON field names are no longer automatically cached in the string constant pool, reducing memory footprint.
8. Module Integration: Three Fewer Dependencies
In Jackson 2.x, to support Java 8's new features, you needed to introduce three additional modules:
<!-- Jackson 2.x requires additional imports -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
In Jackson 3.x, these three modules have been integrated into jackson-databind. No separate introduction is needed.
9. Breaking Changes Quick Reference
I've compiled a quick reference table of the changes in Jackson 3 most likely to cause your code to error:
| Change Type | Jackson 2.x | Jackson 3.x | Impact |
|---|---|---|---|
| Base Exception | JsonProcessingException (Checked) |
JacksonException (Unchecked) |
try-catch needs change |
| ObjectMapper | Mutable, just new it |
Immutable, must use Builder | All creation code needs change |
| Package Name | com.fasterxml.jackson |
tools.jackson |
All imports need change |
| groupId | com.fasterxml.jackson.core |
tools.jackson.core |
pom.xml needs change |
| Date Default | Timestamp | ISO-8601 String | API return format changes |
| Primitive Type null | Allowed (assigns default value) | Throws exception | May cause new errors |
ObjectCodec |
Exists | Removed | Casting code errors |
JsonGenerator.writeObject() |
Exists | Changed to writePOJO() |
Method name needs change |
JsonParser.getCurrentLocation() |
Exists | Changed to currentLocation() |
Method name needs change |
10. Advantages, Disadvantages, and Applicable Scenarios
Advantages of Jackson 3
1. Thread Safety Guaranteed
JsonMapper is immutable; configuration is locked after construction, allowing safe sharing in multi-threaded environments.
2. Modern Java Oriented JDK 17+ baseline allows full utilization of new features like Records and Sealed Classes.
3. Safer Default Configuration Date output in ISO-8601 better conforms to international standards, primitive type null checks are stricter, and polymorphic type validation is stricter.
4. Performance Optimized Multiple optimizations including BeanDescription lazy loading and RecyclerPool lazy initialization.
5. Dependencies Streamlined jackson-module-parameter-names, jackson-datatype-jdk8, jackson-datatype-jsr310 have been integrated into databind.
6. Simpler Exception Handling Unchecked exceptions eliminate the need for try-catch everywhere.
7. Ecosystem Rapidly Catching Up Spring Boot 4 defaults to Jackson 3, and mainstream frameworks like Netflix DGS have already supported it.
Notes on Jackson 3
1. Not an LTS Version Jackson 3.0 is a transitional version; 3.1 is the first LTS version. For production environments, it is recommended to upgrade directly to 3.1.x.
2. Numerous Breaking Changes Package names, class names, method names, exception system, and default configurations have all changed comprehensively; the upgrade workload is significant.
3. Ecosystem Dependencies Lagging Many third-party libraries are still using Jackson 2, such as Swagger. The good news is that Spring Boot 4 manages dependencies for both Jackson 2 and 3, allowing coexistence.
4. Migration Requires Systematic Planning
It is recommended to use the OpenRewrite automated migration tool, or cooperate with Spring's spring.jackson.use-jackson2-defaults configuration for a gradual transition.
Applicable Scenarios
| Scenario | Recommendation Level | Reason |
|---|---|---|
| New projects using Spring Boot 4 | ✅✅✅ Highly Recommended | Spring Boot 4 defaults to Jackson 3 |
| High-concurrency systems pursuing thread safety | ✅✅✅ Highly Recommended | JsonMapper is immutable, thread-safe |
| Needing modern Java features | ✅✅✅ Highly Recommended | Records, Sealed Classes, etc. |
| Old projects upgrading to Spring Boot 4 | ⚠️ Requires Careful Planning | Many breaking changes, recommend using OpenRewrite to assist migration |
| Heavily dependent on Jackson 2 ecosystem libraries | ⚠️ Requires Evaluation | Although coexistence is possible, extra configuration is needed |
11. Migration Suggestions
If you are preparing to upgrade to Jackson 3, I suggest following this order:
Step 1: Upgrade JDK to 17+
This is a hard requirement. If still on JDK 8/11, upgrade the JDK first.
Step 2: Upgrade Spring Boot to 4.x
Spring Boot 4 has fully embraced Jackson 3. Spring Boot 4 manages dependencies for both Jackson 2 and 3, allowing a smooth transition.
Step 3: Use OpenRewrite for Automated Migration
# OpenRewrite provides an automated migration recipe for Jackson 2→3
mvn rewrite:run -DactiveRecipes=org.openrewrite.java.jackson.UpgradeJackson_2_3
Step 4: Utilize Transitional Configuration
Spring Boot 4 provides the spring.jackson.use-jackson2-defaults configuration, which can temporarily maintain Jackson 2's default behavior as a 'safety net' during the transition period.
Step 5: Gradually Replace Code
- Change
import:com.fasterxml.jackson→tools.jackson(except annotations) - Change creation method:
new ObjectMapper()→JsonMapper.builder().build() - Change exception handling:
JsonProcessingException→JacksonException - Check date serialization: Confirm frontend compatibility with ISO-8601 format
For more project practices, visit my technical website: susan.net.cn/project
12. Final Thoughts
Returning to the initial question: Jackson 3 is here, what are the new features?
It's not as simple as adding a few new annotations or fixing a few bugs.
Jackson 3 is a thorough architectural upgrade — from the JDK baseline to package names, from API design to the exception system, from default configurations to performance optimization, everything has been reorganized.
Jackson 3.0 was officially released as a GA version on October 3, 2025. From its inception at the end of 2017 to its official release, it underwent nearly 8 years of refinement.
And Spring Boot 4 has already adopted it as the default JSON library; the entire Java ecosystem is accelerating its migration to Jackson 3.
But be mentally prepared before upgrading — Jackson 3 has a huge number of breaking changes. Package names changed, class names changed, method names changed, exception types changed, and default behaviors changed. This is not a 'seamless upgrade'.
The good news is that Spring Boot 4 manages dependencies for both Jackson 2 and 3, allowing you to migrate gradually. Jackson 2.21 is still an LTS version and will not disappear in the short term.
My suggestion is: New projects should go directly to Jackson 3; old projects should follow the Spring Boot 4 upgrade, utilize spring.jackson.use-jackson2-defaults for transition, and gradually complete the migration. If there are too many dependent libraries that cannot all be upgraded in the short term, coexistence of Jackson 2 and 3 is also a feasible solution.
Top 2 from juejin.cn, machine-translated. The original thread is authoritative.
In practice, it's just one sentence: help me upgrade to Spring Boot 4 and verify. Then go make coffee and wait for the result.
Learned something.