1
0
Files
jackson2-to-3-migration/docs/s04-modules-and-records.md
Ankur f0a7053fc9 Jackson 2 to 3 migration companion code
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.
2026-08-04 23:29:27 +05:30

2.2 KiB

S04 — Modules, records, Optional and dates

Guide: https://ankurm.com/jackson-3-migration-guide/ (Step 5) and the "Dependency Footprint" table

before/S04ModulesAndRecords.java · after/S04ModulesAndRecords.java

The clearest win in the whole migration. Compare the two poms:

jackson2-before/pom.xml — four artifacts plus a compiler flag:

com.fasterxml.jackson.core:jackson-databind
com.fasterxml.jackson.datatype:jackson-datatype-jsr310      <!-- java.time -->
com.fasterxml.jackson.datatype:jackson-datatype-jdk8        <!-- Optional -->
com.fasterxml.jackson.module:jackson-module-parameter-names <!-- records -->
...
<compilerArgs><arg>-parameters</arg></compilerArgs>

jackson3-after/pom.xml — one artifact, no compiler args:

tools.jackson.core:jackson-databind

The three module classes have no tools.jackson equivalent, so leaving the registerModule calls in place is a compile error, not a no-op. Delete them.

Output

Jackson 2 — jackson2-before

with modules    : {"id":1,"departure":"2026-09-15","seat":"12A"}
round-trip      : TravelPlan[id=1, departure=2026-09-15, seat=Optional[12A]]
bare mapper     : FAILS -> InvalidDefinitionException
bare, dates only: FAILS -> InvalidDefinitionException

Jackson 3 — jackson3-after

no modules      : {"id":1,"departure":"2026-09-15","seat":"12A"}
round-trip      : TravelPlan[id=1, departure=2026-09-15, seat=Optional[12A]]
dates           : {"d":"2026-09-15"}
empty Optional  : {"id":2,"departure":"2026-09-16","seat":null}

The first two lines are byte-identical across versions. The difference is everything that had to be set up to get there.

The bare mapper lines in the Jackson 2 output are the point: without the modules, a Jackson 2 mapper cannot serialise Optional or LocalDate at all — it throws InvalidDefinitionException. The Jackson 3 mapper in the "after" file has no modules registered because there are none to register.