1
0
Files
jackson2-to-3-migration/docs/coexistence.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

3.0 KiB

S06 / S08 — Coexistence and wire-format equivalence

Guide: https://ankurm.com/jackson-3-vs-jackson-2/ (FAQs)

The coexistence/ module puts both major versions on one classpath. Its pom.xml declares tools.jackson.core:jackson-databind:3.2.1 and com.fasterxml.jackson.core:jackson-databind:2.22.1 together, with no exclusions and no shading.

S06 — both majors in one class

S06BothOnOneClasspath.java

jackson 2 class : com.fasterxml.jackson.databind.ObjectMapper
jackson 3 class : tools.jackson.databind.json.JsonMapper

jackson 2 output: {"booking_id":1,"travel_date":"2026-09-15"}
jackson 3 output: {"booking_id":1,"travel_date":"2026-09-15"}
wire-compatible : true

3 reads 2's JSON: Booking[id=1, travelDate=2026-09-15]
2 reads 3's JSON: Booking[id=1, travelDate=2026-09-15]

annotation from : jackson-annotations-2.22.jar
  <- one jackson-annotations jar, used by both majors

One DTO, annotated once with @JsonProperty imported from com.fasterxml.jackson.annotation, and both mappers honour it. That is precisely why jackson-annotations deliberately kept the old group ID and package while everything else moved to tools.jackson: it is the shared piece.

The last line resolves which jar the annotation actually came from at runtime — a single jackson-annotations-2.22.jar, serving both.

One thing the guides do not mention: Jackson 3's built-in java.time support does not extend to the Jackson 2 mapper sitting beside it. The coexistence module still needs jackson-datatype-jsr310 and jackson-datatype-jdk8 for the Jackson 2 side. Nothing is shared between them except the annotations.

S08 — wire-format equivalence

S08WireFormatEquivalence.java

The comparison post's final AI prompt asks for a cross-version regression suite. This is that suite, written and run. Ten cases go through both mappers and the JSON strings are compared exactly.

plain record       identical
nested list        identical
map                identical
java.time          identical
Optional present   identical
Optional empty     identical
NON_NULL           identical
@JsonProperty      identical
@JsonFormat        identical
nulls and empties  identical

cases     : 10
mismatches: 0
Wire format is identical across both majors for these cases.

Zero mismatches. The guides' claim that the serialised form is unchanged holds for records, nested lists, maps, java.time, present and empty Optional, NON_NULL inclusion, @JsonProperty renaming, @JsonFormat patterns, and empty collections.

This matters most if your JSON is cached, signed, hashed, or consumed by a system you do not control — those are the cases where a field-order or date-format change would be a production incident rather than a cosmetic difference. Run this suite against your own DTOs before switching such a service.