Gson反序列化列表和类,擦除在这里是如何起作用的?

huangapple go评论73阅读模式
英文:

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

问题

以下是您提供的内容的翻译部分:

我打算编写一个通用方法将 JSON 列表转换为具有类的特定列表这是通用的 JSON 解析器

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, new TypeToken<List<T>>() {}.getType());
    }
}

这是一个我想要将其转换为 JSON然后再转回 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;
    }
}

这是一个简单的测试以查看它是否有效

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());  // 为什么这个有效?

        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()); // 为什么这个无效?
    }
}

控制台输出如下

{"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)

我很难理解为什么 `fromJson(json, class)` 起作用`fromJsonList(json, class)` 不起作用如果应用了擦除那么它不是对两种情况都适用吗为什么第一个方法能够确定类的类型为 `City`,而第二种情况中却是 `LinkedHashMap`?

请注意,我只翻译了您提供的代码和相关的注释,而没有包含问题或其他额外的内容。

英文:

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 &lt;T&gt; String toJson(T object) {
if (object == null) {
return null;
}
return gson.toJson(object);
}
public static &lt;T&gt; T fromJson(String json, Class&lt;T&gt; className) {
if (StringUtils.isEmpty(json)) {
return null;
}
T object = gson.fromJson(json, className);
return object;
}
public static &lt;T&gt; List&lt;T&gt; fromJsonList(String jsonList, Class&lt;T&gt; className) {
// return gson.fromJson(jsonList, TypeToken.getParameterized(List.class, className).getType());
return gson.fromJson(jsonList, new TypeToken&lt;List&lt;T&gt;&gt;() {}.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 &quot;City [city=&quot; + city + &quot;]&quot;;
}
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(&quot;test&quot;);
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&lt;City&gt; list = new LinkedList&lt;&gt;();
list.add(city);
String cityListJson = JsonParserUtils.toJson(list);
System.out.println(cityListJson);
List&lt;City&gt; fromJsonList = JsonParserUtils.fromJsonList(cityListJson, City.class);
System.out.println(fromJsonList.get(0).getCity()); // Why does this not work?
}
}

The console output is as follows:

{&quot;city&quot;:&quot;test&quot;}
test
[{&quot;city&quot;:&quot;test&quot;}]
Exception in thread &quot;main&quot; 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

得分: 1

类型擦除意味着T在运行时丢失,所以

new TypeToken&lt;List&lt;T&gt;&gt;() {}.getType()

变成了

new TypeToken&lt;List&gt;() {}.getType()

这意味着Gson不知道列表元素的类型(City)。

由于它不知道如何将列表中的JSON对象解析为City对象,它将它们解析为Map&lt;String, Object&gt;对象,因此出现了错误消息,提示“无法将Map强制转换为City”。

使用TypeToken.getParameterized()的已注释代码将会起作用,所以坚持使用它。

英文:

Type erasure means that T is lost at runtime, so

new TypeToken&lt;List&lt;T&gt;&gt;() {}.getType()

becomes

new TypeToken&lt;List&gt;() {}.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&lt;String, Object&gt; 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

得分: 0

我看到你的代码中已经有了正确的解决方案,

我认为你在寻找的解决方案是,

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

如果你想知道其中一个为什么有效而另一个为什么无效,
那是因为类型擦除。TypeToken 类在编译时捕获完全已知的类型。

希望能有所帮助。

英文:

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.

huangapple
  • 本文由 发表于 2020年4月10日 13:38:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/61134606.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定