Reputation: 3128
I have some problems with implementation of Json Deserialization in my Android application (with Gson library)
I've made class like this
public class MyJson<T>{
public List<T> posts;
}
And Deserialization call is:
public class JsonDownloader<T> extends AsyncTask<Void, Void, MyJson<T>> {
...
protected MyJson<T> doInBackground(Void... params) {
...
Reader reader = new InputStreamReader(content);
GsonBuilder gson = new GsonBuilder();
Type collectionType = new TypeToken<MyJson<T>>() {}.getType();
result = gson.create().fromJson(reader, collectionType);
...
}
}
Problem is that result.posts list after call holds one Array of LinkedTreeMap Objects(with correct values so problem is Deserialization) instead of MyJson Objects. When I use MyObject instead of T everything is running fine and MyObject is correct.
So is there any way to implement deserialization call without creating custom deserializer?
Upvotes: 71
Views: 111504
Reputation: 11
After reading a lot of comments I found a solution which works for me and also does throw an error due to type erasure.
Below is a sample
public <T> T get(String key, Class<T> type) {
String json = mPref.getString(key, null);
return new Gson().fromJson(json, type);
}
mPref is the object SharedPref replace this with your object
And you can call the above method like this
get("Test", User.class);
Hope this helps.
Upvotes: 1
Reputation: 755
I used the above answer to figure out more generic way in Kotlin (but you can reuse in java with minor adjustment)
I have BaseDB<T>
which loads Table<T>
, while Table has a List<T>
fun parse(jsonString: String): Table<T> {
//this gets the class for the type T
val classT: Class<*> = (javaClass
.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<*>
val type = TypeToken.getParameterized(Table::class.java, classT).type
return GsonHelper.gson.fromJson<Table<T>>(jsonString, type)
}
Upvotes: 1
Reputation: 12352
If you are using gson 2.8.0 or higher, you can use the following method TypeToken#getParametized((Type rawType, Type... typeArguments))
Example:
protected MyJson<T> doInBackground(Class<T> type, String json, Void... params) {
GsonBuilder gson = new GsonBuilder();
Type collectionType = TypeToken.getParameterized(MyJson.class, type).getType();
MyJson<T> myJson = gson.create().fromJson(json, collectionType);
System.out.println(myJson.getPosts()); // [true, false]
return myJson;
}
I believe this would work in your case.
Credits to this answer.
Upvotes: 3
Reputation: 508
For anyone struggling with Kotlin like I did, I've found this way to work
val type = object : TypeToken<MyJson<MyObject>>() { }.type
val response = gson.fromJson<MyJson<MyObject>>(reader, type)
Note that calling a generic function requires the type arguments at the call site after the name of the function (seen here)
Upvotes: 5
Reputation: 1744
So the above answer didn't work for me, after trial and error that's how my code ended:
public class AbstractListResponse<T> {
private List<T> result;
public List<T> getResult() {
return this.result;
}
}
The important part here is the method signature, including the '< T >' on the left.
protected <T> AbstractListResponse<T> parseAbstractResponse(String json, TypeToken type) {
return new GsonBuilder()
.create()
.fromJson(json, type.getType());
}
When calling Gson, the method receives the TypeToken of the generic object.
TypeToken<AbstractListResponse<MyDTO>> typeToken = new TypeToken<AbstractListResponse<MyDTO>>() {};
AbstractListResponse<MyDTO> responseBase = parseAbstractResponse(json, typeToken);
And finally the TypeToken can use MyDTO, or even a simple object, just MyDTO.
Upvotes: 7
Reputation: 32949
Have you tried?
gson.create().fromJson(reader, MyJson.class);
EDIT
After reading this post it seems that you use of Type
is correct. I believe your issue is the use of T
. You must remember that with Java there is type-erasure. This means that at runtime all instances of T
are replaced with Object
. Therefore at runtime what you are passing GSON is really MyJson<Object>
. If you tried this with a concrete class in place of <T>
I believe it would work.
Google Gson - deserialize list<class> object? (generic type)
Upvotes: 25
Reputation: 51711
You have to specify the type of T
at the time of deserialization. How would your List
of posts
get created if Gson
didn't know what Type
to instantiate? It can't stay T
forever. So, you would provide the type T
as a Class
parameter.
Now assuming, the type of posts
was String
you would deserialize MyJson<String>
as (I've also added a String json
parameter for simplicity; you would read from your reader
as before):
doInBackground(String.class, "{posts: [\"article 1\", \"article 2\"]}");
protected MyJson<T> doInBackground(Class<T> type, String json, Void... params) {
GsonBuilder gson = new GsonBuilder();
Type collectionType = new TypeToken<MyJson<T>>(){}.getType();
MyJson<T> myJson = gson.create().fromJson(json, collectionType);
System.out.println(myJson.getPosts()); // ["article 1", "article 2"]
return myJson;
}
Similarly, to deserialize a MyJson
of Boolean
objects
doInBackground(Boolean.class, "{posts: [true, false]}");
protected MyJson<T> doInBackground(Class<T> type, String json, Void... params) {
GsonBuilder gson = new GsonBuilder();
Type collectionType = new TypeToken<MyJson<T>>(){}.getType();
MyJson<T> myJson = gson.create().fromJson(json, collectionType);
System.out.println(myJson.getPosts()); // [true, false]
return myJson;
}
I've assumed MyJson<T>
for my examples to be as
public class MyJson<T> {
public List<T> posts;
public List<T> getPosts() {
return posts;
}
}
So, if you were looking for to deserialize a List<MyObject>
you would invoke the method as
// assuming no Void parameters were required
MyJson<MyObject> myJson = doInBackground(MyObject.class);
Upvotes: 59