Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.ParameterizedType, 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

like image 851
christophmccann Avatar asked Nov 19 '10 15:11

christophmccann


2 Answers

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.

like image 179
BalusC Avatar answered Nov 11 '22 14:11

BalusC


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.

like image 39
StaxMan Avatar answered Nov 11 '22 13:11

StaxMan