28

I am trying to create a generic class for use with Google Gson. I've created the class GsonJsonConverterImplementation<T>. This class has the following method:

public T deserialize(String jsonString) {
    GsonBuilder builder = new GsonBuilder();
    builder.setDateFormat("MM/dd/yy HH:mm:ss");

    Gson gson = builder.create();
    return gson.fromJson(jsonString, T); // T.class etc. what goes here
}

The goal is that this method should be able to work with whatever Type I have set my GsonJsonConverterImplementation to work with. Unfortunately, gson.fromJson(jsonString, T) does not work, nor does using T.class in place of T. I am sure the issue stems from my lack of understanding of Java generic types. What is the correct way of using a generic with Gson?

Edit
Using Kris's answer I would assume that this should work. Unfortunately, clazz cannot be used in this manner and causes a compiler error. What are my options for working with a collection and a generic type with Gson?

public List<T> deserializeList(String jsonString, Class<T> clazz) {
    GsonBuilder builder = new GsonBuilder();
    builder.setDateFormat("MM/dd/yy HH:mm:ss");
    Gson gson = builder.create();

    Type listType = new TypeToken<clazz>(){}.getType(); // compiler error

    return gson.fromJson(jsonString, listType);
}
Community
  • 1
  • 1
ahsteele
  • 26,243
  • 28
  • 134
  • 248
  • 2
    I don't know if you can make this (List -- is ok) work with Gson, since TypeToken is still essentially a static construct (which is why your code can not work). But if you do not absolutely have to use Gson, Jackson does support programmatic creation of types (via TypeFactory methods) and allows what you are trying to achieve. – StaxMan Mar 22 '11 at 19:26
  • Duplicate question: http://stackoverflow.com/questions/4226738/using-generics-with-gson?rq=1 – Mark Butler Feb 23 '13 at 05:12

4 Answers4

44

The simplest approach for what you are attempting is to define the type of class which is to be returned in the method signature itself. You can do so by either passing an instance of 'T' to the method or the Class of the value to be returned. The second approach is the more typical in cases where you expect to generate a return value. Here is an example of this approach using Gson:

public <T> T deserialize(String jsonString, Class<T> clazz) {
    GsonBuilder builder = new GsonBuilder();
    builder.setDateFormat("MM/dd/yy HH:mm:ss");

    Gson gson = builder.create();
    return gson.fromJson(jsonString, clazz);
}

Usage:

MyClass mc = deserialize(jsonString, MyClass.class);
Kris Babic
  • 6,254
  • 1
  • 29
  • 18
  • 1
    Kris while this works, what's the point of setting the generica Type in for the class? Seems to me that `GsonJsonConverterImplementation` should be enough. What am I missing? – ahsteele Mar 20 '11 at 19:50
  • 1
    In my example, there would not be a point unless you make use of it elsewhere. That is one of the problems with Generics and is the main reason you see frameworks that initialize instances using the method I have shown rather than configuring the generic type at the class level. In most cases you are unable to retrieve and use the actually Generic Class type that is defined at the class level. As a result, you are unable to use it to create new instances or to perform any other operation where the Class instance is needed. – Kris Babic Mar 20 '11 at 20:22
  • 1
    As a follow up, if you have subclasses of a class with a Generic type, then there are ways to retrieve that type, however, there are issues with that method as well. Here is a basic example (as implemented from parent class with Generic type) of how you would retrieve the class instance in that case: (Class)((ParameterizedType)this.getClass(). getGenericSuperclass()).getActualTypeArguments()[0] – Kris Babic Mar 20 '11 at 20:24
  • Also, there is a Java library called ClassMate (https://github.com/cowtowncoder/java-classmate) that does this reliably: problem with sub-classing is resolving of all type variables reliably, so that it works with arbitrary number of subtypes, some partially bound. – StaxMan Mar 22 '11 at 19:29
  • After checking about 3…5 different implementations this one is working! Thanks. – Mike Jan 16 '16 at 23:04
  • This works if any one looking for the solution. Thanks. – Muneer Nov 25 '20 at 08:42
4

To get the runtime type of List<T>, given the class of T, it would suck because JDK didn't think we need it so there's no support. You can subclass ParameterizedType, but it really sucks.

    Type typeListT = new ParameterizedType()
    {
        public Type[] getActualTypeArguments()
        {
            return new Type[]{clazz};
        }
        public Type getRawType()
        {
            return List.class;
        }
        public Type getOwnerType()
        {
            return null;
        }
        public boolean equals(Object obj)
        {
            // must implement equals per spec. this sucks
        }
        public int hashCode()
        {
            // this blows! Java forgot to specific it!
            // since we override equals, we should override hashCode
            // we have no idea what value should be returned here!
            // note we are co-existing with other ParameterizedType impls
        }
    };
irreputable
  • 44,725
  • 9
  • 65
  • 93
  • 3
    This is a poor answer, perhaps rather than ranting about the shortcomings of an API you could focus on the technical details. As it stands I don't even see from what you've posted how to use the technique. – Adam Parkin Mar 17 '14 at 22:57
3

I am using these method to read & write Object using GSON.

public static <T> void saveAnyTypeOfObject(String key, T value){
        Gson gson = new Gson();
        String json = gson.toJson(value);
        SharedPref.save(key, json);
    }
    //Type listType = new TypeToken<YourClass>(){}.getType();
    public static <T> T readAnyTypeOfObject(String key, Type tt) {
        Gson gson = new Gson();
        String json = SharedPref.read(key, "{}");
        T obj = gson.fromJson(json, tt);
        return obj;
    }

read it like

  TypeToken<YourClassName> typeToken= new TypeToken<YourClassName>() {};
  YourClassName yourClassName = ListSharedPref.readAnyTypeOfObject("keyForSavingClass", typeToken.getType());

and save it like

YourClassName yourClassName = gson.fromJson(objJsonObject.toString(),YourClassName.class);               
ListSharedPref.saveAnyTypeOfObject("keyForSavingClass",yourClassName);
Zar E Ahmer
  • 33,936
  • 20
  • 234
  • 300
0

If you have serialized a generic object like GenericClass<T> While deserializing you should provide a wrapper class with exact type for example:

public class WrapperClass extends GenericClass<Integer>{
// body is not required
}

then you can call:

GsonInstance.fromJson(string, WrapperClass.class);
Ufuk Bakan
  • 98
  • 7