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); } }