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.
73 lines
3.2 KiB
Markdown
73 lines
3.2 KiB
Markdown
# Part 4 — Custom serialisers, deserialisers and mix-ins
|
|
|
|
Post: <https://ankurm.com/jackson-custom-serializer-mixin/>
|
|
|
|
This is the part with the most API churn. The post's code is Jackson 2 throughout.
|
|
|
|
| Jackson 2 | Jackson 3 |
|
|
|---|---|
|
|
| `JsonSerializer<T>` | `ValueSerializer<T>` |
|
|
| `JsonDeserializer<T>` | `ValueDeserializer<T>` |
|
|
| `SerializerProvider` | `SerializationContext` |
|
|
| `com.fasterxml.jackson.databind.ser.std.StdSerializer` | `tools.jackson.databind.ser.std.StdSerializer` |
|
|
| `gen.writeNumberField` / `writeStringField` | `gen.writeNumberProperty` / `writeStringProperty` |
|
|
| `p.getCodec().readTree(p)` | `ctxt.readTree(p)` |
|
|
| `throws IOException` | removed — exceptions are unchecked |
|
|
| `new SimpleModule(name, Version)` | `new SimpleModule(name)` |
|
|
| `mapper.registerModule(m)` | `builder.addModule(m)` |
|
|
| `mapper.addMixIn(a, b)` | `builder.addMixIn(a, b)` |
|
|
|
|
## E01 / E02 — the handlers
|
|
|
|
[`E01MoneyValueSerializer.java`](../src/main/java/com/ankurm/jackson3/part4custom/E01MoneyValueSerializer.java) ·
|
|
[`E02MoneyValueDeserializer.java`](../src/main/java/com/ankurm/jackson3/part4custom/E02MoneyValueDeserializer.java) ·
|
|
[`Money.java`](../src/main/java/com/ankurm/jackson3/part4custom/Money.java)
|
|
|
|
The deserialiser uses `path()` rather than `get()`. The post's version calls
|
|
`rootNode.get("amount").decimalValue()`, which throws `NullPointerException` on
|
|
`{"currency":"USD"}`. There is a second trap: in Jackson 3, a bare `decimalValue()` on
|
|
a `MissingNode` throws, where Jackson 2 returned `BigDecimal.ZERO`. The defaulting
|
|
overload `decimalValue(BigDecimal.ZERO)` is what you want.
|
|
|
|
## E03 — registration
|
|
|
|
[`E03SimpleModuleRegistration.java`](../src/main/java/com/ankurm/jackson3/part4custom/E03SimpleModuleRegistration.java)
|
|
|
|
```
|
|
serialised : {"amount":20.00,"currency":"USD"}
|
|
amount : 20.0 (scale lost)
|
|
amount exact : 20.00 (scale preserved)
|
|
missing field: Money[amount=0, currencyCode=EUR]
|
|
no module : {"amount":19.999,"currencyCode":"usd"}
|
|
```
|
|
|
|
The post prints `20.00` for the deserialised amount. You get `20.0` — Jackson parses the
|
|
literal as a double before handing it over, so the scale is gone. If scale matters, and
|
|
for money it does, enable `USE_BIG_DECIMAL_FOR_FLOATS`, as the third line shows.
|
|
|
|
## E04 — mix-ins
|
|
|
|
[`E04MixinAnnotations.java`](../src/main/java/com/ankurm/jackson3/part4custom/E04MixinAnnotations.java)
|
|
|
|
```
|
|
with mixin : {"city":"Springfield","street":"123 Main St","zip":"12345"}
|
|
without mixin: {"city":"Springfield","internalTrackingCode":"INTERNAL-X99","postalCode":"12345","street":"123 Main St"}
|
|
```
|
|
|
|
The second line is the proof that nothing was modified at the bytecode level: a mapper
|
|
built without the mix-in still sees `internalTrackingCode` and `postalCode`.
|
|
|
|
## E05 — ValueSerializer directly
|
|
|
|
[`E05ValueSerializerDirect.java`](../src/main/java/com/ankurm/jackson3/part4custom/E05ValueSerializerDirect.java)
|
|
|
|
Beyond the post. `StdSerializer` kept its name, which hides the rename; extending
|
|
`ValueSerializer` directly makes it obvious, and is the leaner form for a simple wrapper.
|
|
|
|
```
|
|
custom : {"assignee":"u-42","title":"Fix build"}
|
|
default : {"assignee":{"value":"u-42"},"title":"Fix build"}
|
|
base class: tools.jackson.databind.ValueSerializer
|
|
```
|
|
|