diff --git a/src/main/java/com/ankurm/jackson3/part0setup/A02SharedMapperConfiguration.java b/src/main/java/com/ankurm/jackson3/part0setup/A02SharedMapperConfiguration.java new file mode 100644 index 0000000..734ed24 --- /dev/null +++ b/src/main/java/com/ankurm/jackson3/part0setup/A02SharedMapperConfiguration.java @@ -0,0 +1,64 @@ +package com.ankurm.jackson3.part0setup; + +import com.fasterxml.jackson.annotation.JsonInclude; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.json.JsonMapper; + +import java.time.LocalDate; + +/** + * Post: Jackson 101 — https://ankurm.com/jackson-java-tutorial/ + * Section: "The ObjectMapper Lifecycle — The One Rule That Matters Most" + * + * Build the mapper ONCE at startup and share it. In Jackson 3 this is enforced + * by the API rather than by convention: the built mapper has no setters at all. + */ +public class A02SharedMapperConfiguration { + + public record Invoice(Long invoiceId, String customerName, LocalDate issuedOn, String note) { } + + /** + * The Jackson 3 equivalent of the classic Spring @Bean ObjectMapper. + * + * Three of the four settings people habitually copy from Jackson 2 tutorials + * are unnecessary or wrong in Jackson 3 — the comments say which and why. + */ + static JsonMapper buildSharedMapper() { + return JsonMapper.builder() + // NOT NEEDED: FAIL_ON_UNKNOWN_PROPERTIES already defaults to false in + // Jackson 3. Listed here only because it is the single most-copied line + // from Jackson 2 configuration; deleting it changes nothing. + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + + // NOT NEEDED: java.time support is built into jackson-databind 3.x and + // dates already serialise as ISO-8601 strings. There is no + // SerializationFeature.WRITE_DATES_AS_TIMESTAMPS in Jackson 3 — the flag + // moved to tools.jackson.databind.cfg.DateTimeFeature and is off by + // default. See beyond/Y03DateTimeDefaults for proof. + + // Skip null fields. Jackson 2's setSerializationInclusion(...) and the + // builder's serializationInclusion(...) do NOT exist in Jackson 3. + // The real API is changeDefaultPropertyInclusion. + .changeDefaultPropertyInclusion( + incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .build(); + } + + public static void main(String[] args) { + JsonMapper mapper = buildSharedMapper(); + + Invoice invoice = new Invoice(500L, "Alice", LocalDate.of(2026, 4, 9), null); + System.out.println("configured : " + mapper.writeValueAsString(invoice)); + + JsonMapper plain = JsonMapper.builder().build(); + System.out.println("defaults : " + plain.writeValueAsString(invoice)); + + // Proof that the mapper is immutable: ObjectMapper in Jackson 3 exposes no + // set*() mutators at all, so there is no way to reconfigure a shared instance. + long setterCount = java.util.Arrays.stream( + tools.jackson.databind.ObjectMapper.class.getMethods()) + .filter(m -> m.getName().startsWith("set")) + .count(); + System.out.println("ObjectMapper set*() methods in Jackson 3: " + setterCount); + } +}