1
0

Jackson 3 series companion code

37 runnable examples covering the eight feature posts on ankurm.com, verified
against Jackson 3.2.1 on Temurin 21.0.5. Every output committed under docs/ was
produced by run-all.sh.

Also documents 11 places where the published snippets do not compile or do not
behave as printed against a real Jackson 3 build - most notably that
writeValueAsString(List<Base>) silently drops the polymorphic type discriminator,
so the post's serialised output cannot be read back.
This commit is contained in:
2026-08-04 23:12:11 +05:30
commit c438afc33b
94 changed files with 3387 additions and 0 deletions

70
docs/part2-modern-java.md Normal file
View File

@@ -0,0 +1,70 @@
# Part 2 — Records, Optionals and sealed types
Post: <https://ankurm.com/jackson-java-records-optionals/>
The single biggest simplification in Jackson 3 lands here. All three features are in
core: the project's [`pom.xml`](../pom.xml) has one dependency, no
`jackson-datatype-jdk8`, no `jackson-module-parameter-names`, and no `-parameters`
compiler argument.
## C01 — records
[`C01RecordRoundTrip.java`](../src/main/java/com/ankurm/jackson3/part2modernjava/C01RecordRoundTrip.java)
```
{"productId":101,"productName":"Wireless Keyboard","unitPrice":49.99}
Wireless Keyboard
round-trip equal: true
```
The post's own dependency block here is wrong twice over: it suggests
`com.fasterxml.jackson.module:jackson-module-parameter-names:3.1.2`, which is both an
artifact Jackson 3 does not need and a version that does not exist at that coordinate.
## C02 — Optional
[`C02OptionalFields.java`](../src/main/java/com/ankurm/jackson3/part2modernjava/C02OptionalFields.java)
```
present : {"customerName":"Alice","middleName":"Marie"}
empty : {"customerName":"Bob","middleName":null}
absent : {"customerName":"Bob"}
isPresent: true
missing -> Optional.empty (null? false)
```
Two behaviours worth committing to memory: an empty `Optional` serialises as `null`
unless you ask for `NON_ABSENT`, and a *missing* property deserialises to
`Optional.empty()` rather than to `null`, so the field is never null.
## C03 — sealed types with an explicit registry
[`C03SealedWithSubTypes.java`](../src/main/java/com/ankurm/jackson3/part2modernjava/C03SealedWithSubTypes.java)
```
Circle with radius: 5.0
Rectangle 10.0 x 4.0
lossy : [{"radius":5.0},{"width":10.0,"height":4.0}]
correct : [{"shapeType":"circle","radius":5.0},{"shapeType":"rectangle","width":10.0,"height":4.0}]
```
Note the `lossy` line. Serialising a `List<Shape>` drops the `shapeType` discriminator,
because a `List` gives Jackson no element type to dispatch on. This is the same defect
covered at length in [Part 5](part5-polymorphic.md).
## C04 — sealed auto-discovery
[`C04SealedAutoDiscovery.java`](../src/main/java/com/ankurm/jackson3/part2modernjava/C04SealedAutoDiscovery.java)
Beyond the post: Jackson 3 reads the `permits` clause, so `@JsonSubTypes` can be dropped
entirely when each permitted type carries `@JsonTypeName`. There is no `@JsonSubTypes`
anywhere in that file, and a third subtype added later just works.
```
Circle -> {"shapeType":"circle","radius":5.0} -> Circle[radius=5.0]
Rectangle -> {"shapeType":"rectangle","width":10.0,"height":4.0} -> Rectangle[width=10.0, height=4.0]
Triangle -> {"shapeType":"triangle","base":3.0,"height":6.0} -> Triangle[base=3.0, height=6.0]
```
In Jackson 2 the `@JsonSubTypes` registry had to be maintained in parallel with
`permits`, and drifted whenever someone added a case.