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.
76 lines
3.0 KiB
Markdown
76 lines
3.0 KiB
Markdown
# S06 / S08 — Coexistence and wire-format equivalence
|
|
|
|
Guide: <https://ankurm.com/jackson-3-vs-jackson-2/> (FAQs)
|
|
|
|
The [`coexistence/`](../coexistence) module puts **both** major versions on one
|
|
classpath. Its [`pom.xml`](../coexistence/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`](../coexistence/src/main/java/com/ankurm/migration/coexist/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`](../coexistence/src/main/java/com/ankurm/migration/coexist/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.
|