Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Type Generic as Argument for GSON

In GSON to get a list of objects you do

Gson gson = new Gson();
Type token = new TypeToken<List<MyType>>(){}.getType();
return gson.fromJson(json, token);

It works great, but I want to go further and have MyType parametrized so I can have a common function to parse list of objects with this code

// the common function 
public <T> List<T> fromJSonList(String json, Class<T> type) {
  Gson gson = new Gson();
  Type collectionType = new TypeToken<List<T>>(){}.getType();
  return gson.fromJson(json, collectionType);
}

// the call
List<MyType> myTypes = parser.fromJSonList(jsonString, MyType.class);

Sadly returns an array of StringMaps, not the type. T is being interpreted as another generic type, not my type. Any workaround ?

like image 486
Rodrigo Asensio Avatar asked Jan 03 '13 12:01

Rodrigo Asensio


People also ask

Which types can be used as arguments of generics?

The actual type arguments of a generic type are. reference types, wildcards, or. parameterized types (i.e. instantiations of other generic types).

What is TypeToken in Java?

protected TypeToken() Constructs a new type literal. Derives represented class from type parameter. Clients create an empty anonymous subclass.

Does Java have generic types?

Java Generics was introduced to deal with type-safe objects. It makes the code stable. Java Generics methods and classes, enables programmer with a single method declaration, a set of related methods, a set of related types.


4 Answers

Since gson 2.8.0, you can use TypeToken#getParametized((Type rawType, Type... typeArguments)) to create the typeToken, then getType() should do the trick.

For example:

TypeToken.getParameterized(List.class, myType.class).getType();
like image 81
oldergod Avatar answered Oct 19 '22 15:10

oldergod


Generics work at compile-time. The reason super-type tokens work, is because (anonymous) inner classes can access the type arguments to their generic superclasses (superinterfaces), which in turn are stored directly in the bytecode metadata.

Once your .java source file is compiled, the type parameter <T> is obviously thrown away. Since it is not known at compile time, it cannot be stored in bytecode, so it's erased and Gson can't read it.

UPDATE

After newacct's answer, I tried to implement what he suggested in his option 2, ie implementing a ParameterizedType. The code looks like this (here is a basic test):

class ListOfSomething<X> implements ParameterizedType {

    private Class<?> wrapped;

    public ListOfSomething(Class<X> wrapped) {
        this.wrapped = wrapped;
    }

    public Type[] getActualTypeArguments() {
        return new Type[] {wrapped};
    }

    public Type getRawType() {
        return List.class;
    }

    public Type getOwnerType() {
        return null;
    }

}

the purpose of this code, is to be used inside getFromJsonList():

public List<T> fromJsonList(String json, Class<T> klass) {
    Gson gson = new Gson();
    return gson.fromJson(json, new ListOfSomething<T>(klass));
}

Even if the technique works and is indeed very clever (I didn't know it and I would have never thinked of it), this is the final accomplishment:

List<Integer> list = new Factory<Integer>()
         .getFromJsonList(text, Integer.class)

instead of

List<Integer> list = new Gson().fromJson(text,
         new TypeToken<List<Integer>>(){}.getType());

To me, all this wrapping in useless, even if I agree that TypeTokens make the code look nasty :P

like image 45
Raffaele Avatar answered Oct 19 '22 16:10

Raffaele


public static final <T> List<T> getList(final Class<T[]> clazz, final String json)
{
    final T[] jsonToObject = new Gson().fromJson(json, clazz);

    return Arrays.asList(jsonToObject);
}

Example:

getList(MyClass[].class, "[{...}]");
like image 27
kayz1 Avatar answered Oct 19 '22 15:10

kayz1


Here is the full code base on great answer from @oldergod

public <T> List<T> fromJSonList(String json, Class<T> myType) {
    Gson gson = new Gson();
    Type collectionType = TypeToken.getParameterized(List.class, myType).getType();
    return gson.fromJson(json, collectionType);
}

Using

List<MyType> myTypes = parser.fromJSonList(jsonString, MyType.class);

Hope it help

like image 42
Linh Avatar answered Oct 19 '22 16:10

Linh