diff --git a/src/main/java/com/ankurm/jackson3/part1objectmapper/B03GenericCollections.java b/src/main/java/com/ankurm/jackson3/part1objectmapper/B03GenericCollections.java new file mode 100644 index 0000000..6bc8fe6 --- /dev/null +++ b/src/main/java/com/ankurm/jackson3/part1objectmapper/B03GenericCollections.java @@ -0,0 +1,54 @@ +package com.ankurm.jackson3.part1objectmapper; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.json.JsonMapper; + +import java.util.List; +import java.util.Map; + +/** + * Post: Jackson ObjectMapper Guide — https://ankurm.com/jackson-objectmapper-guide/ + * Section: "Working with Collections and Generic Types" + * + * Why TypeReference is required, and what actually happens without it. + * + * Note the import: TypeReference lives in tools.jackson.core.type in Jackson 3. + */ +public class B03GenericCollections { + + public record Article(Long articleId, String title) { } + + public static void main(String[] args) { + JsonMapper mapper = JsonMapper.builder().build(); + + String jsonArray = "[{\"articleId\":1,\"title\":\"First\"}," + + "{\"articleId\":2,\"title\":\"Second\"}]"; + + // CORRECT: TypeReference captures List
through an anonymous subclass, + // so the parameterised type survives erasure and reaches Jackson at runtime. + List
articles = mapper.readValue(jsonArray, new TypeReference>() { }); + System.out.println("size : " + articles.size()); + System.out.println("element class : " + articles.get(0).getClass().getSimpleName()); + System.out.println("first title : " + articles.get(0).title()); + + // WRONG: List.class erases the element type. This COMPILES and does not throw + // here — the failure is deferred to the first time you treat an element as an + // Article, which is what makes it such an unpleasant bug. + @SuppressWarnings("rawtypes") + List raw = mapper.readValue(jsonArray, List.class); + System.out.println("raw element : " + raw.get(0).getClass().getSimpleName() + + " <- not Article"); + try { + Article boom = (Article) raw.get(0); + System.out.println("unreachable: " + boom); + } catch (ClassCastException e) { + System.out.println("cast fails : ClassCastException, as expected"); + } + + // A Map value type needs the same treatment. + String jsonObject = "{\"a\":{\"articleId\":9,\"title\":\"Nine\"}}"; + Map byKey = + mapper.readValue(jsonObject, new TypeReference>() { }); + System.out.println("map value : " + byKey.get("a").title()); + } +}