Part 1: TypeReference, and what List.class actually produces
This commit is contained in:
@@ -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<Article> through an anonymous subclass,
|
||||||
|
// so the parameterised type survives erasure and reaches Jackson at runtime.
|
||||||
|
List<Article> articles = mapper.readValue(jsonArray, new TypeReference<List<Article>>() { });
|
||||||
|
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<String, Article> byKey =
|
||||||
|
mapper.readValue(jsonObject, new TypeReference<Map<String, Article>>() { });
|
||||||
|
System.out.println("map value : " + byKey.get("a").title());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user