8116dd0810b5a36e78fe8ab7a61fd2bf4f7855ad
Three Maven modules - jackson2-before (2.22.1), jackson3-after (3.2.1) and a coexistence module with BOTH majors on one classpath - so every claim in the two migration guides is executed rather than asserted. Paired class names make the before/after outputs directly diffable via run-all.sh. Confirms the guides on wire-format equivalence (10-case suite, zero mismatches), classpath coexistence and the collapse of four artifacts into one. Corrects nine points, including that enableDefaultTyping() is still present in Jackson 2.22.1 rather than removed in 2.16, and that the published "after" mapper snippet does not compile.
jackson2-to-3-migration
Companion code for the two Jackson migration guides on ankurm.com:
The point of this repo is that nothing here is asserted. Every migration claim is a program that runs on both major versions, so you can diff the outputs.
Jackson 2 2.22.1 (com.fasterxml.jackson.core)
Jackson 3 3.2.1 (tools.jackson.core)
jackson-annotations 2.22 (shared by both)
JDK Temurin 21.0.5
Maven 3.9.9
Layout
| Module | What it is |
|---|---|
jackson2-before/ |
Jackson 2.22.1, with the three extra modules and the -parameters compiler flag it needs |
jackson3-after/ |
Jackson 3.2.1. One dependency, no modules, no compiler flags |
coexistence/ |
Both majors on one classpath, used together in one class |
Paired examples share a class name across before and after, so their captured
outputs sit side by side and diff cleanly.
Run it
git clone https://ankurm.com/git.app/asmhatre/jackson2-to-3-migration.git
cd jackson2-to-3-migration
./run-all.sh
run-all.sh builds all three modules, runs every example, writes real stdout to
docs/output/<module>-<Class>.txt, and then reports which before/after pairs differ.
Diff any pair yourself:
diff docs/output/before-S07DefaultsThatFlipped.txt \
docs/output/after-S07DefaultsThatFlipped.txt
The examples
| Pair | Migration topic | Guide section |
|---|---|---|
S01MapperConstruction |
new ObjectMapper() → JsonMapper.builder(), and immutability |
Steps 2–3 |
S02ExceptionHierarchy |
JsonProcessingException (checked) → JacksonException (unchecked) |
"A Silent Production Bug" |
S03CustomHandlers |
JsonSerializer/JsonDeserializer → ValueSerializer/ValueDeserializer |
"Class Renames" |
S04ModulesAndRecords |
Four artifacts and a compiler flag collapse to one dependency | Step 5, "Dependency Footprint" |
S05DefaultTyping |
activateDefaultTyping moves to the builder |
Step 4, security guide |
S07DefaultsThatFlipped |
Defaults that changed silently | "Removed and Changed Default Features" |
S06BothOnOneClasspath (coexistence) |
Both majors in one JVM, one shared annotations jar | Comparison FAQ |
S08WireFormatEquivalence (coexistence) |
10-case regression suite asserting byte-identical JSON | Comparison FAQ |
Corrections to the guides
Building this against real artifacts surfaced several places where the guides are wrong. Each is demonstrated by code.
| # | Claim in the guide | What actually happens |
|---|---|---|
| 1 | .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) in the "after" snippet |
Does not compile. Not on SerializationFeature in Jackson 3 — moved to tools.jackson.databind.cfg.DateTimeFeature, and already off by default. See after/S01. |
| 2 | JsonMapper.builder().serializationInclusion(NON_NULL) |
No such builder method. Use changeDefaultPropertyInclusion(UnaryOperator). |
| 3 | mapper.activateDefaultTyping(validator, ObjectMapper.DefaultTyping.NON_FINAL, ...) |
Two errors: the method is not on the Jackson 3 mapper (builder only), and DefaultTyping is now a top-level enum. See after/S05. |
| 4 | enableDefaultTyping() was "removed in 2.16" |
Still present on ObjectMapper in Jackson 2.22.1, deprecated. before/S05 prints the reflective proof. This matters: the method survives your 2.x upgrades and only breaks at Jackson 3. |
| 5 | import tools.jackson.core.JsonFactory |
Wrong package — it is tools.jackson.core.json.JsonFactory. |
| 6 | "Java baseline: Java 11 minimum" (migration guide) | The comparison post says 17, and 17 is correct for all core modules. The migration guide's Java 11 figure is Jackson 2's. |
| 7 | jackson-annotations version 3.0 / 2.20.0 |
Jackson 3.2.1 resolves jackson-annotations 2.22. There is no released 3.0 — only release candidates. Import tools.jackson:jackson-bom and let it pick. |
| 8 | "catch (IOException) blocks silently stop catching" |
Only when the block also does real I/O. If it contains nothing but Jackson calls, it is a compile error and the compiler catches it for you. Both cases in after/S02. |
| 9 | Removing AUTO_DETECT_CREATORS breaks single-arg constructors |
The constant is gone; the behaviour is not. Demonstrated in the features repo. |
What the guides get right
Worth stating, since the list above is all corrections. These claims were tested and hold:
- The wire format is identical.
S08WireFormatEquivalenceruns 10 cases — records, nested lists, maps,java.time, present and emptyOptional,NON_NULL,@JsonProperty,@JsonFormat, empty collections — through both mappers and asserts string equality. Zero mismatches. Downstream consumers are unaffected by the upgrade. - Both majors coexist on one classpath.
S06BothOnOneClasspathuses both in one class, and each reads the other's output. - Annotations do not change. A single
jackson-annotations-2.22.jarserves both. - Records,
Optionalandjava.timeneed no modules in Jackson 3.before/S04shows a bare Jackson 2 mapper failing on all three;after/S04shows the same code working withJsonMapper.builder().build()and nothing else. - Mapper immutability is real, not advisory.
before/S01mutates a shared mapper after construction and the change sticks;after/S01showsObjectMapperhas zeroset*methods.
Related
- jackson3-by-example — 37 runnable examples for the eight feature posts.
Description
Side-by-side Jackson 2.22.1 and Jackson 3.2.1 code for the ankurm.com migration guides. Three Maven modules — jackson2-before, jackson3-after and a coexistence module that puts BOTH majors on one classpath — so every migration claim is executed rather than asserted. Paired class names make before/after outputs directly diffable.
Languages
Java
95.4%
Shell
4.6%