GSON的奇怪行为

时间:2014-04-15 13:37:52

标签: java json serialization model gson

我在我的一个项目中使用了名为Gson的Google JSON库。

我有一个使用GSON将JSON String转换为对象的代码。我有以下方法来做到这一点:

public static <T> ApiResponse<T> fromJson(String json)
{
    return new Gson().fromJson(json, new TypeToken<ApiResponse<T>>() {}.getType());
}

当我这样做时似乎工作正常:

ApiResponse<List<JobModel>> response = ApiResponse.fromJson(new String(bytes));

OR

ApiResponse<Double> response = ApiResponse.fromJson(new String(bytes));

但是当我尝试这样做时:

ApiResponse<JobModel> response = ApiResponse.fromJson(new String(bytes));

如果JobModel是我自己的类,我会收到以下错误:

com.google.gson.internal.LinkedTreeMap无法转换为com.pcf.api.model.JobModel

然后我在ApiResponse中实现了另一种方法:

public static <T> ApiResponse<T> fromJson(String json, TypeToken<ApiResponse<T>> token)
{
    return new Gson().fromJson(json, token.getType());
}

这次用上面的函数调用它:

ApiResponse<JobModel> response = ApiResponse.fromJson(new String(bytes), new TypeToken<ApiResponse<JobModel>>() {});

似乎工作正常。

我无法理解这一点,因为两个函数完全相同。唯一的区别是,首先它纯粹依赖于Java的泛型,在第二个中我将TypeToken作为参数传递。

任何人都可以解释我为什么会发生这种情况,有没有办法解决它?

2 个答案:

答案 0 :(得分:1)

TypeToken是一种带有泛型的黑客攻击。它取决于使用匿名或普通类继承类型,并使用Class#getGenericSuperclass()表示状态

  

如果超类是参数化类型,则返回Type对象   必须准确反映源中使用的实际类型参数   代码。

换句话说,在像这样的匿名类声明中

new TypeToken<ApiResponse<T>>() {}.getType())

超类是TypeToken<ApiResponse<T>>。它相当于

class Subclass extends TypeToken<ApiResponse<T>>

假设T在范围内。因此,当您致电Class#getGenericSuperclass()时,它会返回知道ParameterizedType ApiReponse<T>,因为这是源代码中使用的实际类型参数。

使用

中的任何一个调用原始函数时
ApiResponse<List<JobModel>> response = ApiResponse.fromJson(new String(bytes));
ApiResponse<Double> response = ApiResponse.fromJson(new String(bytes));
ApiResponse<JobModel> response = ApiResponse.fromJson(new String(bytes));

虽然编译器会推断并将相应的类型绑定为方法调用的类型参数,但该方法的内部将使用TypeToken传递相同的ApiResponse<T>对象。由于Gson不知道T是什么,它将使用默认值,该默认值取决于它在JSON中看到的内容。如果它看到一个对象,它将使用LinkedTreeMap。如果它看到一个数字原语,它将使用double。等

如果您传递TypeToken

ApiResponse.fromJson(new String(bytes), new TypeToken<ApiResponse<JobModel>>() {});

它相当于

class Subclass extends TypeToken<ApiResponse<JobModel>>

换句话说,Class#getGenericSuperclass()将返回ParameterizedType ApiResponse<JobModel>。 Gson可以提取JobModel并将其用作反序列化JSON的提示。

  

任何人都可以解释我为什么会发生这种情况并且有什么方法可以解决   解决它?

没有什么可以解决的。这就是它的工作原理。

补充阅读:

答案 1 :(得分:0)

泛型在编译时工作,由于Java中缺少具体的泛型(不可能做T t = new T()),Gson本身被迫使用TypeToken方法,如你所见。否则Gson会以更优雅的方式完成它。