# jackson2-to-3-migration Companion code for the two Jackson migration guides on [ankurm.com](https://ankurm.com): - [Jackson 2 to Jackson 3 Migration Guide](https://ankurm.com/jackson-3-migration-guide/) - [Jackson 3 vs Jackson 2: Complete Comparison](https://ankurm.com/jackson-3-vs-jackson-2/) 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/`](jackson2-before) | Jackson 2.22.1, with the three extra modules and the `-parameters` compiler flag it needs — see [pom.xml](jackson2-before/pom.xml) | | [`jackson3-after/`](jackson3-after) | Jackson 3.2.1. One dependency, no modules, no compiler flags — see [pom.xml](jackson3-after/pom.xml) | | [`coexistence/`](coexistence) | **Both majors on one classpath**, used together in one class — see [pom.xml](coexistence/pom.xml) and [docs/coexistence.md](docs/coexistence.md) | Paired examples share a class name across `before` and `after`, so their captured outputs sit side by side and diff cleanly. ## Run it ```bash 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/-.txt`, and then reports which before/after pairs differ. Diff any pair yourself: ```bash diff docs/output/before-S07DefaultsThatFlipped.txt \ docs/output/after-S07DefaultsThatFlipped.txt ``` ## The examples | Pair | Migration topic | Write-up | |---|---|---| | `S01MapperConstruction` | `new ObjectMapper()` → `JsonMapper.builder()`, and immutability | [docs/s01-mapper-construction.md](docs/s01-mapper-construction.md) | | `S02ExceptionHierarchy` | `JsonProcessingException` (checked) → `JacksonException` (unchecked) | [docs/s02-exception-hierarchy.md](docs/s02-exception-hierarchy.md) | | `S03CustomHandlers` | `JsonSerializer`/`JsonDeserializer` → `ValueSerializer`/`ValueDeserializer` | [docs/s03-custom-handlers.md](docs/s03-custom-handlers.md) | | `S04ModulesAndRecords` | Four artifacts and a compiler flag collapse to one dependency | [docs/s04-modules-and-records.md](docs/s04-modules-and-records.md) | | `S05DefaultTyping` | `activateDefaultTyping` moves to the builder | [docs/s05-default-typing.md](docs/s05-default-typing.md) | | `S07DefaultsThatFlipped` | Defaults that changed silently | [docs/s07-defaults-that-flipped.md](docs/s07-defaults-that-flipped.md) | | `S06BothOnOneClasspath` | Both majors in one JVM, one shared annotations jar | [docs/coexistence.md](docs/coexistence.md) | | `S08WireFormatEquivalence` | 10-case regression suite asserting byte-identical JSON | [docs/coexistence.md](docs/coexistence.md) | Each write-up shows the Jackson 2 and Jackson 3 output side by side, with the source files linked. Start at [docs/README.md](docs/README.md) for the index, or dive straight into [docs/s07-defaults-that-flipped.md](docs/s07-defaults-that-flipped.md) — the silent behavioural changes are the ones that cost the most to find later. Raw stdout for every program is in [docs/output/](docs/output). ## 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`](jackson3-after/src/main/java/com/ankurm/migration/after/S01MapperConstruction.java). | | 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`](jackson3-after/src/main/java/com/ankurm/migration/after/S05DefaultTyping.java). | | 4 | `enableDefaultTyping()` was "removed in 2.16" | Still present on `ObjectMapper` in Jackson 2.22.1, deprecated. [`before/S05`](jackson2-before/src/main/java/com/ankurm/migration/before/S05DefaultTyping.java) 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`](jackson3-after/src/main/java/com/ankurm/migration/after/S02ExceptionHierarchy.java). | | 9 | Removing `AUTO_DETECT_CREATORS` breaks single-arg constructors | The constant is gone; the behaviour is not. Demonstrated in the [features repo](https://ankurm.com/git.app/asmhatre/jackson3-by-example). | ## 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.** `S08WireFormatEquivalence` runs 10 cases — records, nested lists, maps, `java.time`, present and empty `Optional`, `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.** `S06BothOnOneClasspath` uses both in one class, and each reads the other's output. - **Annotations do not change.** A single `jackson-annotations-2.22.jar` serves both. - **Records, `Optional` and `java.time` need no modules in Jackson 3.** `before/S04` shows a bare Jackson 2 mapper failing on all three; `after/S04` shows the same code working with `JsonMapper.builder().build()` and nothing else. - **Mapper immutability is real, not advisory.** `before/S01` mutates a shared mapper after construction and the change sticks; `after/S01` shows `ObjectMapper` has zero `set*` methods. ## Related - [jackson3-by-example](https://ankurm.com/git.app/asmhatre/jackson3-by-example) — 37 runnable examples for the eight feature posts.