Gson desearilize list and class, how does erasure work here?

大兔子大兔子 提交于 2021-02-11 00:22:14

问题


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?


回答1:


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.




回答2:


I saw that the correct solution is already there in your code,

I think he solution you are looking for is,

Type type = TypeToken
                .getParameterized(List.class, className)
                .getType();
        return gson.fromJson(jsonList, type);

If you want to know why one works and other don't, That is because of the type erasure. TypeToken class captures types that are fully known at compile time.

Hope it helps.



来源:https://stackoverflow.com/questions/61134606/gson-desearilize-list-and-class-how-does-erasure-work-here

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!