part4custom: add E06 (ValueDeserializer direct) and E07 (mix-in on a record), fix run-all.sh exit-code detection through the grep filter (post #50)

This commit is contained in:
2026-10-08 17:00:53 +00:00
parent 0157132e9d
commit 84070a8dfb
6 changed files with 130 additions and 6 deletions
@@ -0,0 +1,65 @@
package com.ankurm.jackson3.part4custom;
import tools.jackson.core.JsonParser;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.databind.ValueDeserializer;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.module.SimpleModule;
/**
* BEYOND THE POST — the deserialising half of E05ValueSerializerDirect.
*
* E05 showed ValueSerializer<T> written directly, without the StdSerializer detour.
* This is the other half: ValueDeserializer<T> written directly, without StdDeserializer.
* Together they round-trip UserId as a bare JSON string ("u-42") instead of the
* structural {"value":"u-42"} a record would otherwise produce.
*/
public class E06ValueDeserializerDirect {
public record UserId(String value) { }
static class UserIdSerializer extends tools.jackson.databind.ValueSerializer<UserId> {
@Override
public void serialize(UserId id, tools.jackson.core.JsonGenerator gen,
tools.jackson.databind.SerializationContext ctxt) {
gen.writeString(id.value());
}
}
/** Reads the bare JSON string straight back into the wrapper type. */
static class UserIdDeserializer extends ValueDeserializer<UserId> {
@Override
public UserId deserialize(JsonParser p, DeserializationContext ctxt) {
return new UserId(p.getString());
}
}
public record Ticket(UserId assignee, String title) { }
public static void main(String[] args) {
SimpleModule module = new SimpleModule("UserIdModule");
module.addSerializer(UserId.class, new UserIdSerializer());
module.addDeserializer(UserId.class, new UserIdDeserializer());
JsonMapper mapper = JsonMapper.builder().addModule(module).build();
Ticket original = new Ticket(new UserId("u-42"), "Fix build");
String json = mapper.writeValueAsString(original);
System.out.println("serialised : " + json);
Ticket restored = mapper.readValue(json, Ticket.class);
System.out.println("round-trip : " + restored);
System.out.println("equal : " + original.equals(restored));
// Without the module, Jackson can't turn a bare JSON string "u-42" into a
// one-component record - it expects {"value":"u-42"} and this throws.
JsonMapper plain = JsonMapper.builder().build();
try {
plain.readValue("{\"assignee\":\"u-42\",\"title\":\"Fix build\"}", Ticket.class);
System.out.println("no module : (unexpectedly succeeded)");
} catch (Exception e) {
System.out.println("no module : " + e.getClass().getSimpleName()
+ " - " + e.getMessage().lines().findFirst().orElse(""));
}
}
}
@@ -0,0 +1,44 @@
package com.ankurm.jackson3.part4custom;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import tools.jackson.databind.json.JsonMapper;
/**
* BEYOND THE POST — does addMixIn() still work when the third-party type is a
* record, not a field-and-getter class like E04's Address?
*
* A record's "properties" are its canonical accessor methods (postalCode(), not
* getPostalCode()), so a mixin has to declare methods with those exact names for
* Jackson's annotation-introspection merge to find them. Run this before assuming
* either way - the answer below was obtained by actually running it, not inferred
* from how E04's field-based mixin behaves.
*/
public class E07MixinOnRecord {
/** Stand-in for a third-party record type whose source you cannot modify. */
public record AddressRecord(String street, String city, String postalCode,
String internalTrackingCode) { }
/** Mix-in for AddressRecord: method signatures, not fields, to match a record's accessors. */
public interface AddressRecordMixin {
@JsonIgnore
String internalTrackingCode();
@JsonProperty("zip")
String postalCode();
}
public static void main(String[] args) {
AddressRecord address = new AddressRecord(
"123 Main St", "Springfield", "12345", "INTERNAL-X99");
JsonMapper withMixin = JsonMapper.builder()
.addMixIn(AddressRecord.class, AddressRecordMixin.class)
.build();
System.out.println("with mixin : " + withMixin.writeValueAsString(address));
JsonMapper withoutMixin = JsonMapper.builder().build();
System.out.println("without mixin: " + withoutMixin.writeValueAsString(address));
}
}