I intend to write a generic method to convert a json list into it's specific list with class. This is the generic json parser:
public class JsonParserUtils {
private static Gson gson = new Gson();
public static <T> String toJson(T object) {
if (object == null) {
return null;
}
return gson.toJson(object);
}
public static <T> T fromJson(String json, Class<T> className) {
if (StringUtils.isEmpty(json)) {
return null;
}
T object = gson.fromJson(json, className);
return object;
}
public static <T> List<T> fromJsonList(String jsonList, Class<T> className) {
// return gson.fromJson(jsonList, TypeToken.getParameterized(List.class, className).getType());
return gson.fromJson(jsonList, new TypeToken<List<T>>() {}.getType());
}
}
Here is a dummy class I'd like to convert to Json and back to Pojo.
public class City {
private String city;
public City(String city) {
this.city = city;
}
@Override
public String toString() {
return "City [city=" + city + "]";
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
Here is a simple test to see if it works:
public class TestParser {
public static void main(String[] args) {
City city = new City("test");
String cityJson = JsonParserUtils.toJson(city);
System.out.println(cityJson);
City fromJson = JsonParserUtils.fromJson(cityJson, City.class);
System.out.println(fromJson.getCity()); // Why does this work?
List<City> list = new LinkedList<>();
list.add(city);
String cityListJson = JsonParserUtils.toJson(list);
System.out.println(cityListJson);
List<City> fromJsonList = JsonParserUtils.fromJsonList(cityListJson, City.class);
System.out.println(fromJsonList.get(0).getCity()); // Why does this not work?
}
}
The console output is as follows:
{"city":"test"}
test
[{"city":"test"}]
Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.jtraq.hospital.vos.City
at com.jtraq.hospital.vos.TestParser.main(TestParser.java:24)
I'm struggling to understand why fromJson(json, class)
works but fromJsonList(json, class)
doesn't. If erasure applies, then doesn't it apply to both cases? Why is it that first method figures out the class is of type City
and not LinkedHashMap
like in the second case?
Type erasure means that T
is lost at runtime, so
new TypeToken<List<T>>() {}.getType()
becomes
new TypeToken<List>() {}.getType()
which means that Gson doesn't know the list element type (City
).
Since it doesn't know to parse the JSON objects in the list into City
objects, it parses them into Map<String, Object>
objects, hence the error message saying "Map
cannot be cast to City
".
The commented code using TypeToken.getParameterized()
will work, so just stick with that.