Reputation: 2814
I have this code:
Type typeOfObjectsList = new TypeToken<ArrayList<myClass>>() {}.getType();
List<myClass> objectsList = new Gson().fromJson(json, typeOfObjectsList);
It converts a JSON string to a List
of objects.
But now I want to have this ArrayList
with a dynamic type (not just myClass
), defined at runtime.
The ArrayList
's item type will be defined with reflection.
I tried this:
private <T> Type setModelAndGetCorrespondingList2(Class<T> type) {
Type typeOfObjectsListNew = new TypeToken<ArrayList<T>>() {}.getType();
return typeOfObjectsListNew;
}
But it doesn't work. This is the exception:
java.sql.SQLException: Fail to convert to internal representation: {....my json....}
Upvotes: 99
Views: 140145
Reputation: 193
My Solution;
JsonUtil.class
package dev.agitrubard.util;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.experimental.UtilityClass;
import java.util.Arrays;
import java.util.List;
@UtilityClass
public class JsonUtil {
private static final Gson GSON = new GsonBuilder().create();
public static <T> List<T> fromJsonArray(String jsonArray, Class<T[]> clazz) {
return Arrays.asList(
GSON.fromJson(jsonArray, clazz)
);
}
}
Main.class
public class Main {
public static void main(String[] args) {
String json = "[{\"id\":1,\"firstName\":\"John\",\"lastName\":\"Doe\"},{\"id\":2,\"firstName\":\"Jane\",\"lastName\":\"Doe\"}]";
List<User> users = JsonUtil.fromJsonArray(json, User[].class);
for (User user : users) {
System.out.println(user.getId() + " - " + user.getFirstName() + " " + user.getLastName());
}
}
@Getter
public static class User {
private Long id;
private String firstName;
private String lastName;
}
}
Console Output
1 - John Doe
2 - Jane Doe
Upvotes: 0
Reputation: 541
this code is json string data to convert in arraylist model
String json = getIntent().getExtras().getString("submitQuesList", "");
Type typeOfObjectsList = new TypeToken<ArrayList<Datum_ques>>() {}.getType();
submitQuesList = new Gson().fromJson(json, typeOfObjectsList);
Upvotes: 0
Reputation: 280122
The syntax you are proposing is invalid. The following
new TypeToken<ArrayList<Class.forName(MyClass)>>
is invalid because you're trying to pass a method invocation where a type name is expected.
The following
new TypeToken<ArrayList<T>>()
is not possible because of how generics (type erasure) and reflection works. The whole TypeToken
hack works because Class#getGenericSuperclass()
does the following
Returns the Type representing the direct superclass of the entity (class, interface, primitive type or void) represented by this Class.
If the superclass is a parameterized type, the Type object returned must accurately reflect the actual type parameters used in the source code.
In other words, if it sees ArrayList<T>
, that's the ParameterizedType
it will return and you won't be able to extract the compile time value that the type variable T
would have had.
Type
and ParameterizedType
are both interfaces. You can provide an instance of your own implementation (define a class that implements either interface and overrides its methods) or use one of the helpful factory methods that TypeToken
provides in its latest versions. For example,
private Type setModelAndGetCorrespondingList2(Class<?> typeArgument) {
return TypeToken.getParameterized(ArrayList.class, typeArgument).getType();
}
Upvotes: 56
Reputation: 1530
This is straightforward & simple in Kotlin.
val typeDataModelClass = Array<DataModelClass>::class.java
Upvotes: 1
Reputation: 146
Well actually i have made extension functions to resolve this , saving a list to SharedPrefrence and retrieve them in anyplace in the app like this :
use this to Save a List to SharedPref. For example :
fun <T> SaveList(key: String?, list: List<T>?) {
val gson = Gson()
val json: String = gson.toJson(list)
getSharedPrefrences(App.getAppContext() as Application).edit().putString(key, json).apply()}
return the list in any place from sharedPref. like this :
fun Context.getList(key: String): String? {
return getSharedPrefrences(App.getAppContext() as Application).getString(key, null)}
inline fun <reified T : Any> String?.fromJson(): T? = this?.let {
val type = object : TypeToken<T>() {}.type
Gson().fromJson(this, type)}
Usage in Saving and Getting List from Saved one is like :
saveList(Consts.SLIDERS, it.data)
SetSliderData(this.getList(Consts.SLIDERS).fromJson<MutableList<SliderResponseItem>>()!!)
Upvotes: 0
Reputation: 31
in kotlin you can
inline fun <reified T> parseData(row :String): T{
return Gson().fromJson(row, object: TypeToken<T>(){}.type)
}
Upvotes: 3
Reputation: 351
inline fun <reified T : Any> T?.json() = this?.let { Gson().toJson(this, T::class.java) }
Example to use :
val list = listOf("1","2","3")
val jsonArrayAsString = list.json()
//output : ["1","2","3"]
val model= Foo(name = "example",email = "[email protected]")
val jsonObjectAsString = model.json()
//output : {"name" : "example", "email" : "[email protected]"}
inline fun <reified T : Any> String?.fromJson(): T? = this?.let {
val type = object : TypeToken<T>() {}.type
Gson().fromJson(this, type)
}
Example to use :
val jsonArrayAsString = "[\"1\",\"2\",\"3\"]"
val list = jsonArrayAsString.fromJson<List<String>>()
val jsonObjectAsString = "{ "name" : "example", "email" : "[email protected]"}"
val model : Foo? = jsonObjectAsString.fromJson()
//or
val model = jsonObjectAsString.fromJson<Foo>()
Upvotes: 9
Reputation: 690
Fully working solution:
String json = .... //example: mPrefs.getString("list", "");
ArrayList<YouClassName> myTypes = fromJSonList(json, YouClassName.class);
public <YouClassName> ArrayList<YouClassName> fromJSonList(String json, Class<YouClassName> type) {
Gson gson = new Gson();
return gson.fromJson(json, TypeToken.getParameterized(ArrayList.class, type).getType());
}
Upvotes: 1
Reputation: 15010
Since Gson 2.8.0, you can use TypeToken#getParameterized(Type rawType, Type... typeArguments)
to create the TypeToken
, then getType()
should do the trick.
For example:
TypeToken.getParameterized(ArrayList.class, myClass).getType()
Upvotes: 92
Reputation: 50726
You can do this with Guava's more powerful TypeToken
:
private static <T> Type setModelAndGetCorrespondingList2(Class<T> type) {
return new TypeToken<ArrayList<T>>() {}
.where(new TypeParameter<T>() {}, type)
.getType();
}
Upvotes: 7
Reputation: 14731
sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
workes. No need for custom implementation
Type type = ParameterizedTypeImpl.make(List.class, new Type[]{childrenClazz}, null);
List list = gson.fromJson(json, type);
Can be used with maps and any other collection:
ParameterizedTypeImpl.make(Map.class, new Type[]{String.class, childrenClazz}, null);
Here is nice demo how you can use it in custom deserializer: Deserializing ImmutableList using Gson
Upvotes: 5
Reputation: 72331
You can actually do it. You just need to parse first your data into an JsonArray
and then transform each object individually, and add it to a List
:
Class<T> dataType;
//...
JsonElement root = jsonParser.parse(json);
List<T> data = new ArrayList<>();
JsonArray rootArray = root.getAsJsonArray();
for (JsonElement json : rootArray) {
try {
data.add(gson.fromJson(json, dataType));
} catch (Exception e) {
e.printStackTrace();
}
}
return data;
Upvotes: 2
Reputation: 331
Option 1 - implement java.lang.reflect.ParameterizedType
yourself and pass it to Gson.
private static class ListParameterizedType implements ParameterizedType {
private Type type;
private ListParameterizedType(Type type) {
this.type = type;
}
@Override
public Type[] getActualTypeArguments() {
return new Type[] {type};
}
@Override
public Type getRawType() {
return ArrayList.class;
}
@Override
public Type getOwnerType() {
return null;
}
// implement equals method too! (as per javadoc)
}
Then simply:
Type type = new ListParameterizedType(clazz);
List<T> list = gson.fromJson(json, type);
Note that as per javadoc, equals method should also be implemented.
Option 2 - (don't do this) reuse gson internal...
This will work too, at least with Gson 2.2.4.
Type type = com.google.gson.internal.$Gson$Types.newParameterizedTypeWithOwner(null, ArrayList.class, clazz);
Upvotes: 33
Reputation: 369
This worked for me:
public <T> List<T> listEntity(Class<T> clazz)
throws WsIntegracaoException {
try {
// Consuming remote method
String strJson = getService().listEntity(clazz.getName());
JsonParser parser = new JsonParser();
JsonArray array = parser.parse(strJson).getAsJsonArray();
List<T> lst = new ArrayList<T>();
for(final JsonElement json: array){
T entity = GSON.fromJson(json, clazz);
lst.add(entity);
}
return lst;
} catch (Exception e) {
throw new WsIntegracaoException(
"WS method error [listEntity()]", e);
}
}
Upvotes: 8