# Part 4 — Custom serialisers, deserialisers and mix-ins Post: This is the part with the most API churn. The post's code is Jackson 2 throughout. | Jackson 2 | Jackson 3 | |---|---| | `JsonSerializer` | `ValueSerializer` | | `JsonDeserializer` | `ValueDeserializer` | | `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 ```