Using generics with GSON
I am using GSON to decode JSON into an object of type T e.g.
public T decode(String json) {
Gson gson = new Gson();
return gson.fromJson(json, new TypeToken<T>() {}.getType());
}
This however returns an exception -
java.lang.AssertionError: Unexpected type. Expected one of: java.lang.reflect.ParameterizedTy开发者_C百科pe, java.lang.reflect.GenericArrayType, but got: sun.reflect.generics.reflectiveObjects.TypeVariableImpl, for type token: T
I thought that by using TypeToken I avoided Type Erasure.
Am I wrong?
Thanks
First of all, I fail to see how it's useful to wrap Gson like that.
As to your problem, the information about generic type T
itself is not available during runtime. It's been erased. It's only available during compile time. You want to parameterize it with the actual type instead like new TypeToken<List<String>>
.
Due to lack of reified Generics in Java (it isn't possible to do a T t = new T()
), Gson itself is forced to use the TypeToken
approach, as you see. Otherwise Gson would have done it in a much more elegant manner.
In order to be able to pass the actual type around, you've to reinvent the same thing as TypeToken
is already doing. And this makes no sense :) Just reuse it or just use Gson straight without wrapping it in some helper class like that.
I think the first answer is not pointing out the actual solution: you MUST also pass Class instance along with T, like so:
public T decode(String json, Class<T> cls) {
Gson gson = new Gson();
return gson.fromJson(json, cls);
}
This is because 'T' here is a type VARIABLE, not a type reference; and only used by compiler to add implicit casts and verify type compatibility. But if you pass actual class it can be used; and compiler will check type compatibility to reduce chance of mismatch.
Alternatively you could take in TypeToken and pass it; but TypeToken must be constructed with real type, not a type variable; type variable is of little use here. But if you do want to wrap things you wouldn't want caller to use TypeToken (which is a Gson type).
Same wrapping mechanism would work with other libs like Jackson, which you mentioned.
My solution to this was to use the json parser and break it into pieces
public static <TT> PushObj<TT> fromJSON(String json, Class<TT> classType)
{
JsonObject jObj = new JsonParser().parse(json).getAsJsonObject();
String type = jObj.get("type").getAsString();
JsonObject job = jObj.get("object").getAsJsonObject();
TT obj = new Gson().fromJson(job, classType);
return new PushObj<TT>(type, obj);
}
Where the object structure is: {String:Type, Generic:Object}
And the variables are: jObj is the JSONObject of the string passed in and job is the JSONObject of the generic object
So i used the json parser to get the type separately, and reflection for the object.
精彩评论