Java, Gson, Lists and Generics - java

I have seen some solution to this problem as long as lists are not involved, so I am pushing my luck in order to see if something can be done.
I'm looking to factorize some heavy duplicated code using generics. I have trouble who are probably related to type-erasure. First of all, here is a sample of duplicated code:
private void readsFoo() throws Exception {
JsonArray jsonArray = getJsonArray(foo_field);
Type listType = new TypeToken<List<Foo>>() {
}.getType();
List<Foo> fooList= gson.fromJson(jsonArray, listType);
for (Foo foo : fooList) {
.....
}
}
private void readsGoo() throws Exception {
JsonArray jsonArray = getJsonArray(goo_field);
Type listType = new TypeToken<List<Goo>>() {
}.getType();
List<Goo> gooList= gson.fromJson(jsonArray, listType);
for (Goo goo : gooList) {
.....
}
}
Now, here is the code I produced myself:
private void readsFoo() throws Exception {
JsonArray jsonArray = getJsonArray(foo_field);
List<Foo> fooList = getElementsList(jsonArray);
for (Foo foo: fooList ) {
.....
}
}
private <T> List<T> getElementsList(JsonArray iArray)
{
Type listType = new TypeToken<List<T>>() {}.getType();
validateJsonElement(listType, "In getElementsList: Unable to find field TypeTokens");
List<T> list = gson.fromJson(iArray, listType);
validateJsonElement(list, "In getElementsList: Unable to find list from Json");
return list;
}
At runtime, I got the following error: java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to ....json.Foo
Is there any way to solve this problem ? Because frankly I do hate not reusable code.
Thanks !

Basically, a TypeToken can not be made with generic type. You could pass it as an argument instead:
private void readsFoo() throws Exception {
JsonArray jsonArray = getJsonArray(foo_field);
List<Foo> fooList = getElementsList(jsonArray, new TypeToken<List<Foo>>(){});
for (Foo foo: fooList ) {
.....
}
}
private <T> List<T> getElementsList(JsonArray iArray, TypeToken<List<T>> tt)
{
Type listType = tt.getType();
validateJsonElement(listType, "In getElementsList: Unable to find field TypeTokens");
List<T> list = gson.fromJson(iArray, listType);
validateJsonElement(list, "In getElementsList: Unable to find list from Json");
return list;
}
You are right about the erasure. Since T is erased, the TypeToken you create will hold the type List<T> instead of List<Foo>, which does not hold any information.

Related

java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.Object[]

In my android project, I want to create a generic class to store all types of entities in sharedPreferences but give the error :
java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.Object[]
here is my code :
public ArrayList<E> getItems(Context context) {
android.content.SharedPreferences settings;
List<E> objects ;
settings = context.getSharedPreferences(PREF_NAME,
Context.MODE_PRIVATE);
if (settings.contains(ITEM_NAME)) {
String json = settings.getString(ITEM_NAME, null);
GsonBuilder gson = new GsonBuilder();
Type collectionType = new TypeToken<List<E>>(){}.getType();
E[] items = gson.create().fromJson(json,
collectionType); // in this line occurs error
objects = new ArrayList<E>(Arrays.asList(items));
} else
return null;
return (ArrayList<E>) objects;
}
Try:
List<E> items = gson.create().fromJson(json, collectionType);
That exception is complaining that you had an ArrayList object (which was returned from fromJson()), but you attempted to cast it to an array with that assignment to E[] items.

List<Type> and java.lang.ClassCastException: com.google.gson.internal.StringMap

Below my function,
public <T> List<T> jsonToListBean(String json, Class<T> clazz) {
Gson gson = new Gson();
Type listType = new TypeToken<List<T>>(){}.getType();
List<T> returnValue = gson.fromJson(json, listType);
return returnValue;
}
but the system returns this exception:
java.lang.ClassCastException: com.google.gson.internal.StringMap
I notice also that if I use the following line code
Type listType = new TypeToken<ArrayList<ShapeBean>>(){}.getType();
it works, but my target is to be absolutly as generic as possible to make my code more efficient.
How should i change my code to make usable also passing the type "T" instead the "ShapeBean"?
Solved. Thanks to kan's link below the code:
public <T> List<T> listEntity(String jsonCommand, Class<T> clazz) {
List<T> lst = new ArrayList<T>();
try {
// Consuming remote method
JsonParser parser = new JsonParser();
JsonArray array = parser.parse(jsonCommand).getAsJsonArray();
for(final JsonElement json: array){
Gson g = new Gson();
T entity = g.fromJson(json, clazz);
lst.add(entity);
}
} catch (Exception e) {
e.printStackTrace();
} return lst;
}
check also this:
Gson TypeToken with dynamic ArrayList item type

Gson deserialize generic list into a generic method

When I have a list inside a generic method Gson returns a list of Object instead a list of generic type.
I've seen a lot of threads with no solution, and if I dont use a generic method would have to create a method for each bean.
Is there any one with any idea what do I have to do so solve it?
PS: For a while I've created loop into the list to serialize entity by entity, splitting returned String and deserializing entity by entity, but clearly it's a workaround
Creating a generic list and serializing to JSON (this is a webservices method):
public String listEntity(String nomeClasse) throws WsException {
// Identifying the entity class
Class<?> clazz = Class.forName(nomeClasse);
// Querying with Hibernate
List<?> lst = getDao().listEntity(clazz);
// Check if is null
if (lst == null) {
return "[]";
}
return gson.toJson(lst);
}
Consuming the Webservice method:
public <T> List<T> listEntity(Class<T> clazz)
throws WsIntegracaoException {
try {
// Consuming remote method
String strJson = getService().listEntity(clazz.getName());
Type type = new TypeToken<List<T>>() {}.getType();
// HERE IS THE PROBLEM
List<T> lst = GSON.fromJson(strJson, type);
// RETURNS IS A LIST OF OBJECT INSTEAD OF A LIST OF <T>
return lst;
} catch (Exception e) {
throw new WsIntegracaoException(
"WS method error [listEntity()]", e);
}
}
Invoking the generic method:
List<City> list = listEntity(City.class);
// Here I get a ClassCastException
fillTable(list);
List Element (wrong):
java.lang.Object#23f6b8
Exception:
java.lang.ClassCastException: java.lang.Object cannot be cast to java.io.Serializable
SOLUTION - THIS WORKED FOR ME: Gson TypeToken with dynamic ArrayList item type
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);
}
}

GSON parse generic Json Array

My question is very similiar to this:
Unable to parse Json array using Gson
But I cann't get the answer from it.
The answer from above link:
public static List<MapData> getData(){
Gson gson = new Gson();
String jsonString = "[{\"id\":18,\"city\":\"test\",\"street\":\"test 1\",\"zipcode\":121209,\"state\":\"IL\",\"lat\":32.158138,\"lng\":34.807838},{\"id\":19,\"city\":\"test\",\"street\":\"1\",\"zipcode\":76812,\"state\":\"IL\",\"lat\":32.161041,\"lng\":34.810410}]";
Type type = new TypeToken<List<MapData>>(){}.getType();
return gson.fromJson(jsonString, type);
}
It works well, but I want to use implicit operator on generic type. See below:
public static <T> List<T> getData(Class<T> classT){
Gson gson = new Gson();
String jsonString = "[{\"id\":18,\"city\":\"test\",\"street\":\"test 1\",\"zipcode\":121209,\"state\":\"IL\",\"lat\":32.158138,\"lng\":34.807838},{\"id\":19,\"city\":\"test\",\"street\":\"1\",\"zipcode\":76812,\"state\":\"IL\",\"lat\":32.161041,\"lng\":34.810410}]";
Type type = new TypeToken<List<T>>(){}.getType();
return gson.fromJson(jsonString, type);
}
And then I try to pass the Class argument to the method:
List<MapData> data = getData(MapData.class);
System.out.println(data.get(0).city);
Then an error was arised:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.ssc.ctq.nav.util.MapData
Can anyone tell me why I get this error? Is implicit operator is not supported in TypeToken class?
you can do like this:
Gson gson = new Gson();
String jsonString = "[{\"id\":18,\"city\":\"test\",\"street\":\"test 1\",\"zipcode\":121209,\"state\":\"IL\",\"lat\":32.158138,\"lng\":34.807838},{\"id\":19,\"city\":\"test\",\"street\":\"1\",\"zipcode\":76812,\"state\":\"IL\",\"lat\":32.161041,\"lng\":34.810410}]";
List<Map> tmpList = gson.fromJson(jsonString);
List<T> resultList = new Arraylist<T>(tmplist.size());
for(Map map:tmpList){
String tmpJson = gson.toJson(map);
resultList.add(gson.fromJson(tmpJson, classT));
}
return resultList;
I met the same problem. From the Javadoc of TypeToken:
This syntax cannot be used to create type literals that have wildcard parameters, such as Class<?> or List<? extends CharSequence>.
You must explicitly indicate the type of T in TypeToken<T>, without generics.
You can use this method in order to parse generic json string to map
public Map<String, String> getMapFromJson(String jsonString) {
Map<String, String> map = new HashMap<>();
try {
JSONObject object = new JSONObject(jsonString);
Iterator<?> iterator = object.keys();
while (iterator.hasNext()) {
String key = (String) iterator.next();
if(!key.isEmpty() && !object.getString(key).isEmpty()){
map.put(key, object.getString(key));
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return map;
}

conversion between list of objects and Gson string

I'm converting a list of objects to json string and then back to list of objects as below:
List<Work> list = new ArrayList<Work>();
Work w1 = new Work();
Work w2 = new Work();
list.add(w1);
list.add(w2);
String str = GsonUtils.getJsonString(list);
Type listType = new TypeToken<ArrayList<Work>>() {
}.getType();
List<Work> list1 = new Gson().fromJson(str, listType);
this works completely fine and returns the list List<Work>.
Now I'm doing same by extracting a method as below:
List<Work> list = new ArrayList<Work>();
Work w1 = new Work();
Work w2 = new Work();
list.add(w1);
list.add(w2);
String str = GsonUtils.getJsonString(list);
Type listType = new TypeToken<ArrayList<Work>>() {
}.getType();
List<Work> list1 = getListFromJSON(str, Work.class);
where method is defined as below:
public <T> List<T> getListFromJSON(String str, Class<T> type) {
Type listType = new TypeToken<ArrayList<T>>() {
}.getType();
List<T> list = new Gson().fromJson(str, listType);
return list;
}
this time it's resulting into an error:
Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.StringMap cannot be cast to com.restfb.types.User$Work
at expt.ExptGsonList.main(ExptGsonList.java:45)
Please help me to know where am I going wrong and how can I get this working using method?
Change the method signature to
public <T> List<T> getListFromJSON(String str, Type type)
and pass listType

Categories