Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gson, ClassCastException with Generics

I have below code to deserialize the json array and it worked find. However, if I try to iterate the list, I am getting ClassCastException. If I replace generic type T with MyObj and the iteration works. But I wanted to make the deserialization code to be generic. Could you please help me how I can fix this error? Thanks in advance.

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to MyObj

json deserialization code

public static <T> List<T> mapFromJsonArray(String respInArray) {
    Type listType = new TypeToken<ArrayList<T>>(){}.getType();
    List<T> ret = new Gson().fromJson(respInArray, listType);
    return ret;
}

error at the for loop.

List<MyObj> myObjResponse = JsonUtil.<MyObj>mapFromJsonArray(jsonResponse);
for(MyObj obj : myObjResponse){ // Class cast exception 
    //do something
}
like image 702
user800799 Avatar asked Sep 30 '22 01:09

user800799


2 Answers

You certainly need to give mapFromJsonArray more information than this--that is unavoidable, since the binding of <T> for each invocation is entirely erased at runtime. All your TypeToken is doing is trying to reify information that you don't have.

But all you need to do is pass in the element type, and this will work:

public static <T> List<T> mapFromJsonArray(String respInArray, Class<T> elementClass) {
    Type listType = new TypeToken<List<T>>(){}
               .where(new TypeParameter<T>(){}, elementClass).getType();
    return new Gson().fromJson(respInArray, listType);
}

It's not any more clunky to call than what you had:

List<MyObj> myObjResponse = mapFromJsonArray(jsonResponse, MyObj.class);
like image 185
Mark Peters Avatar answered Oct 20 '22 00:10

Mark Peters


The TypeToken is a trick to acquire type information for a generic type at runtime. However by passing it a generic type parameter (T), due to type erasure what you're effectively doing is:

Type listType = new TypeToken<ArrayList<Object>>(){}.getType();

This isn't providing Gson with the necessary information it needs in order to deserialise into the list correctly, hence it does the deserialisation incorrectly.

One solution might be to pass the type token as a parameter to mapFromJsonArray :

public static <T> List<T> mapFromJsonArray(String respInArray, Type listType) {
    List<T> ret = new Gson().fromJson(respInArray, listType);
    return ret;
}

List<MyObj> myObjResponse = JsonUtil.<MyObj>mapFromJsonArray(
    jsonResponse, new TypeToken<ArrayList<MyObj>>(){}.getType());

for(MyObj obj : myObjResponse){
    //do something
}
like image 36
jon-hanson Avatar answered Oct 19 '22 23:10

jon-hanson