65

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 ?

Rodrigo Asensio
  • 2,760
  • 4
  • 26
  • 26
  • Maybe these links can help: http://stackoverflow.com/questions/75175/create-instance-of-generic-type-in-java http://stackoverflow.com/questions/356583/can-i-reflectively-instantiate-a-generic-type-in-java http://www.velocityreviews.com/forums/t593591-how-to-create-an-instance-of-type-t.html http://tutorials.jenkov.com/java-reflection/generics.html In the second link, they suggest a factory method. Hmm... I'm not sure if using ? instead of T could solve the problem. – ajlopez Jan 03 '13 at 13:34

13 Answers13

63

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();
oldergod
  • 15,033
  • 7
  • 62
  • 88
41

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

Raffaele
  • 20,627
  • 6
  • 47
  • 86
  • It is. The key point to understand is that in order for the *super-type token machinary* to work, we must create a ficticious anonymous class, ie a piece of bytecode that has even its own `Outer.$N.class` file. This is the trick that makes `TypeToken` work, but can't really work in your case, because when the supplied method source is compiled, the `Class` argument fo `fromJSonList` is erased – Raffaele Jan 03 '13 at 14:10
  • It's not "erased" in the `Type`. Rather, the generic argument will be stored as a `TypeVariable` rather than a `Class` – newacct Jan 03 '13 at 19:50
  • @newacct maybe you misunderstood that phrase. I mean, inside `fonrJsonList()`, the `type` argument is a raw `Class`. – Raffaele Jan 03 '13 at 20:47
  • @Raffaele: but they are not using `type` to construct the `collectionType`. In fact, they are not using `type` at all – newacct Jan 03 '13 at 21:00
  • @newacct indeed, because OP introduced it just as a way to match the signature with the return type. But then he couldn't use it inside the method body, that's why asked this question. And I think it's correct to state that there's no way to access `T` because it's erased. Where do you think is the flaw? – Raffaele Jan 03 '13 at 21:06
  • @Raffaele: but at runtime we do have the class object for `T` -- it is `type`. It's just that at compile time we don't have it. – newacct Jan 03 '13 at 21:10
  • @newacct We all agree on this. Maybe you are questioning the inaccuracy of the phrase *...the Class argument to fromJSonList() is erased* in my first comment. Yeah, that's very badly phrased and uses wrong terms. Maybe I should have written that `Class` is not reifiable, but this doesn't really help in a comment. It's an unfortunate sentence, wrong and misleading. But comments cannot be edited – Raffaele Jan 03 '13 at 21:43
  • 1
    Souldn't `getRawType` return `ListOfSomething.class` instead? Otherwise thanks, you saved my ass today :) – Benoit Duffez Apr 19 '14 at 15:49
  • @Raffaele What if i have to use `List` inside another Generic class lets say `Response` which also contains other properties. I am so sorry if i sound too noobish, i recently shifted from C# to java and it is literally over my head right now... – Hmxa Mughal Dec 16 '14 at 16:10
  • You can use the code in this answer if the type is known at runtime . If you need further help, ask your own question along with the demo code – Raffaele Dec 16 '14 at 18:54
  • What is the namespace for Factory? – Vincent May 03 '16 at 04:58
  • I have a a class that wraps backend calls that retreive both single items and lists of items. This class is then used for several different types of objects. This answer IS useful because this technique removes the need to supply both a Class and a TypeToken> parameter to my class, because I can create a ListOfSomething *from* the former. Makes my code cleaner and less error-prone. – JHH Apr 04 '17 at 13:40
30
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, "[{...}]");
kayz1
  • 7,260
  • 3
  • 53
  • 56
13

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

Linh
  • 57,942
  • 23
  • 262
  • 279
6

I've taken Raffaele's approach one step further and generified the class, so that it works with every class A, where B is a non-parameterized class. Might be useful for Sets and other Collections.

    public class GenericOf<X, Y> implements ParameterizedType {

    private final Class<X> container;
    private final Class<Y> wrapped;

    public GenericOf(Class<X> container, Class<Y> wrapped) {
        this.container = container;
        this.wrapped = wrapped;
    }

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

    public Type getRawType() {
        return container;
    }

    public Type getOwnerType() {
        return null;
    }

}
Community
  • 1
  • 1
Christian Brüggemann
  • 2,152
  • 17
  • 23
5

In Kotlin you can simply use this function:

inline fun <reified T> fromJson(json: String): T {
  return Gson().fromJson(json, object: TypeToken<T>(){}.type)
}

and use it like

val myTypes: List<MyType> = fromJson(jsonString);

It will parse any object including gereric types as List. Keyword inline and reified ensures that type will not be erased.

For detail info I can recommend this Medium post

goofy
  • 425
  • 7
  • 13
3

This has been answered in previous questions. Basically, there are 2 options:

  1. Pass the Type in from the calling site. The calling code will use TypeToken or whatever to construct it.
  2. Construct a Type corresponding to the parameterized type yourself. This will require you to write a class that implements ParameterizedType
newacct
  • 119,665
  • 29
  • 163
  • 224
  • I think option 1 completely defeats the purpose of `fromJsonList()` - option 2 is very clever instead. Is [this](http://pastebin.com/PV6cP9jc) what you mean? – Raffaele Jan 03 '13 at 21:26
  • @Raffaele: perhaps. Or you can copy the source of ParameterizedTypeImpl (used internally by many Java libraries) from the Internet – newacct Jan 04 '13 at 00:29
3

If programming in kotlin, we can use reified type parameter in inline function

class GenericGson {

    companion object {
        inline fun <reified T : Any> Gson.fromJsonTokenType(jsonString: String): T {
            val type = object : TypeToken<T>() {}.type
            return this.fromJson(jsonString, type)
        }

        inline fun <reified T : Any> Gson.fromJsonType(jsonString: String): T = this.fromJson(jsonString, T::class.java)

        inline fun <reified T : Any> fromJsonTokenType(jsonString: String): T = Gson().fromJsonTokenType(jsonString)

        inline fun <reified T : Any> fromJsonType(jsonString: String): T = Gson().fromJsonType(jsonString)
    }
}

And use like below in your code

val arrayList = GenericGson.fromJsonTokenType<ArrayList<Person>>(json)
alijandro
  • 11,627
  • 2
  • 58
  • 74
0

Kotlin "ListOfSomething" solution that worked for me:

fun <T: Any> getGsonList(json: String, kclass: KClass<T>) : List<T> {

    return getGsonInstance().fromJson<List<T>>(json, ListOfSomething<T>(kclass.java))
}


internal class ListOfSomething<X>(wrapped: Class<X>) : ParameterizedType {

    private val wrapped: Class<*>

    init {
        this.wrapped = wrapped
    }

    override fun getActualTypeArguments(): Array<Type> {
        return arrayOf<Type>(wrapped)
    }

    override fun getRawType(): Type {
        return ArrayList::class.java
    }

    override fun getOwnerType(): Type? {
        return null
    }
}
Michael Peterson
  • 10,383
  • 3
  • 54
  • 51
0

In kotlin simple use for example:

Get places function

fun getPlaces<T> (jsonString : String, clazz: Class<T>) : T { val places : T = Gson().fromJson(jsonString,clazz) return places }

Then you can use as:

val places = getPlaces(Array<Place>::class.java)
Cristian Cardoso
  • 665
  • 6
  • 11
0
  public static <T> T getObject(String gsonStr) {
        Gson gson = new GsonBuilder()
                .setLenient()
                .create();
        Type collectionType = new TypeToken< T>(){}.getType();
        return gson.fromJson(gsonStr,
                collectionType);
    }

When use:

Class1 class1=  getObject(jsonStr);
Ahmad Aghazadeh
  • 16,571
  • 12
  • 101
  • 98
0

This work for everything. e.g. map which has a key and value generic.

CustomType type = new CustomType(Map.class, String.class, Integer.class);

So no more TokenType.

class CustomType implements ParameterizedType {
    private final Class<?> container;
    private final Class<?>[] wrapped;

    @Contract(pure = true)
    public CustomType(Class<?> container, Class<?>... wrapped) {
        this.container = container;
        this.wrapped = wrapped;
    }

    @Override
    public Type[] getActualTypeArguments() {
        return this.wrapped;
    }

    @Override
    public Type getRawType() {
        return this.container;
    }

    @Override
    public Type getOwnerType() {
        return null;
    }
}
Eric Chan
  • 49
  • 3
0
public <T> List<T> fromJSonList(String json) {
  Gson gson = new Gson();
  Type collectionType = new TypeToken<List<T>>(){}.getType();
  return gson.fromJson(json, collectionType);
}

//Just call
List<MyType> myTypes = parser.<MyType>fromJSonList(jsonString);
tangfuhao
  • 21
  • 2