In Java generics are more or less gone at runtime. This is called type erasure. So your HashMap<String, Object> will just be HashMap<?, ?> when your programme runs. So Jackson cannot determine the types Stringand Object. That is why you have to create a TypeReference which will preserve that information at runtime so Jackson can deserialise your JSON String into the correct classes.
If you don't specify a type Jackson will still be able to correctly deserialise simple objects like String or numbers but when a more complex object is encountered it will simply deserialise them into LinkedHashMap which will then most likely lead to a ClassCastException when you try to access the deserialised object.
In Java generics are more or less gone at runtime. This is called type erasure. So your HashMap<String, Object> will just be HashMap<?, ?> when your programme runs. So Jackson cannot determine the types Stringand Object. That is why you have to create a TypeReference which will preserve that information at runtime so Jackson can deserialise your JSON String into the correct classes.
If you don't specify a type Jackson will still be able to correctly deserialise simple objects like String or numbers but when a more complex object is encountered it will simply deserialise them into LinkedHashMap which will then most likely lead to a ClassCastException when you try to access the deserialised object.
This TypeReference<T> implements Comparable<TypeReference<T>> is part of jackon library. And this is part of com.fasterxml.jackson.core.type. From the API documentation:
This generic abstract class is used for obtaining full generics type information by sub-classing; it must be converted to ResolvedType implementation (implemented by JavaType from "databind" bundle) to be used. Class is based on ideas from http://gafter.blogspot.com/2006/12/super-type-tokens.html, Additional idea (from a suggestion made in comments of the article) is to require bogus implementation of Comparable (any such generic interface would do, as long as it forces a method with generic type to be implemented). to ensure that a Type argument is indeed given. Usage is by sub-classing: here is one way to instantiate reference to generic type List:
TypeReference ref = new TypeReference<List>() { }; which can be passed to methods that accept TypeReference, or resolved using TypeFactory to obtain ResolvedType.
This is basically used to de-serialize the Generic type of JSON objects, for example the JAVA Collection objects as below:
public List<Double> getDoubleList() {
return getObjectFromJson(new TypeReference<List<Double>>() {
});
}
public <T> T getObjectFromJson(String jsonString, final TypeReference<T> typeReference) {
try {
return new ObjectMapper().readValue(jsonString, typeReference);
} catch (JsonProcessingException e) {
// TODO
}
}
Writing a jackson type reference
java - Jackson and generic type reference - Stack Overflow
java - Using TypeReference with Jackson - Stack Overflow
json - Java/Jackson - how does TypeReference() actually work? - Stack Overflow
I'm using a jackson object mapper to deserialize json. If I need to get a List<String>, I need to pass a type reference because neither java nor Kotlin let me write List<String>.class. In java the type reference is
new TypeReference<List<String>>() {}while in Kotlin is
object : TypeReference<List<String>>() {}And this is the only simple case when the kotlin code is longer than the java code. And it is also as horrible as the java version
Am I doing something wrong?
Since what jackson really needs is a type, I wish we could have a special syntax for this case, kotlink could let us write generic-type-of(List<Integer>) and return a nice type...
This is a well-known problem with Java type erasure: T is just a type variable, and you must indicate actual class, usually as Class argument. Without such information, best that can be done is to use bounds; and plain T is roughly same as 'T extends Object'. And Jackson will then bind JSON Objects as Maps.
In this case, tester method needs to have access to Class, and you can construct
JavaType type = mapper.getTypeFactory().
constructCollectionType(List.class, Foo.class)
and then
List<Foo> list = mapper.readValue(new File("input.json"), type);
'JavaType' works !!
I was trying to unmarshall (deserialize) a List in json String to ArrayList java Objects and was struggling to find a solution since days.
Below is the code that finally gave me solution.
Code:
JsonMarshallerUnmarshaller<T> {
T targetClass;
public ArrayList<T> unmarshal(String jsonString) {
ObjectMapper mapper = new ObjectMapper();
AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();
mapper.getDeserializationConfig()
.withAnnotationIntrospector(introspector);
mapper.getSerializationConfig()
.withAnnotationIntrospector(introspector);
JavaType type = mapper.getTypeFactory().
constructCollectionType(
ArrayList.class,
targetclass.getClass());
try {
Class c1 = this.targetclass.getClass();
Class c2 = this.targetclass1.getClass();
ArrayList<T> temp = (ArrayList<T>)
mapper.readValue(jsonString, type);
return temp ;
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null ;
}
}
No, you can not use type variables like T this way with TypeReference: you must pass actual parameterization. This is because there is no runtime information whatsoever associated with T here: it is just a placeholder. So it is effectively treated as java.lang.Object.
But you can build structured types programmatically using TypeFactory (instance of which is available via ObjetMapper.getTypeFactory()). You will still need Class instances that represent key and value types, but with this information it is possible to construct JavaType values that contain full type information that you need.
you can do like this
public T fromMap(S map, TypeReference<T> typeReference) {
ObjectMapper objectMapper = new ObjectMapper();
T obj = objectMapper.convertValue(map, typeReference);
return obj;
}