使用 GSON 反序列化泛型类型

问题描述 投票:0回答:7

我在 Android 应用程序(使用 Gson 库)中实现 Json 反序列化时遇到一些问题

我上过这样的课

public class MyJson<T>{
    public List<T> posts;
}

反序列化调用是:

public class JsonDownloader<T> extends AsyncTask<Void, Void, MyJson<T>> {
...
protected MyJson<T> doInBackground(Void... params) {
  ...
    Reader reader = new InputStreamReader(content);
    GsonBuilder gson = new GsonBuilder();
    Type collectionType = new TypeToken<MyJson<T>>() {}.getType();
    result = gson.create().fromJson(reader, collectionType);
  ...
  }
}

问题是调用后的 result.posts 列表保存一个 LinkedTreeMap 对象数组(具有正确的值,因此问题是反序列化)而不是 MyJson 对象。当我使用 MyObject 而不是 T 时,一切运行正常并且 MyObject 是正确的。

那么有没有什么方法可以在不创建自定义反序列化器的情况下实现反序列化调用?

java android json deserialization gson
7个回答
57
投票

您必须在反序列化时指定

T
的类型。如果
List
不知道要实例化什么
posts
,那么如何创建
Gson
Type
?它不可能永远停留在
T
。因此,您可以提供类型
T
作为
Class
参数。

现在假设

posts
的类型是
String
,您会将
MyJson<String>
反序列化为(为了简单起见,我还添加了
String json
参数;您可以像以前一样从
reader
中读取):

doInBackground(String.class, "{posts: [\"article 1\", \"article 2\"]}");

protected MyJson<T> doInBackground(Class<T> type, String json, Void... params) {

    GsonBuilder gson = new GsonBuilder();
    Type collectionType = new TypeToken<MyJson<T>>(){}.getType();

    MyJson<T> myJson = gson.create().fromJson(json, collectionType);

    System.out.println(myJson.getPosts()); // ["article 1", "article 2"]
    return myJson;
}

类似地,反序列化

MyJson
Boolean
对象

doInBackground(Boolean.class, "{posts: [true, false]}");

protected MyJson<T> doInBackground(Class<T> type, String json, Void... params) {

    GsonBuilder gson = new GsonBuilder();
    Type collectionType = new TypeToken<MyJson<T>>(){}.getType();

    MyJson<T> myJson = gson.create().fromJson(json, collectionType);

    System.out.println(myJson.getPosts()); // [true, false]
    return myJson;
}

我假设

MyJson<T>
我的例子是

public class MyJson<T> {

    public List<T> posts;

    public List<T> getPosts() {
        return posts;
    }
}

因此,如果您想要反序列化

List<MyObject>
,您可以调用该方法作为

// assuming no Void parameters were required
MyJson<MyObject> myJson = doInBackground(MyObject.class);

24
投票

你尝试过吗?

gson.create().fromJson(reader, MyJson.class);

编辑

阅读this帖子后,您似乎使用

Type
是正确的。我相信您的问题是使用
T
。您必须记住,Java 存在类型擦除。这意味着在运行时,所有
T
实例都将替换为
Object
。因此,在运行时,您传递给 GSON 的实际上是
MyJson<Object>
。如果您尝试用具体的类代替
<T>
我相信它会起作用。

Google Gson - 反序列化列表对象? (通用型)


7
投票

所以上面的答案对我来说不起作用,经过反复试验,这就是我的代码的结束方式:

public class AbstractListResponse<T> {
    private List<T> result;

    public List<T> getResult() {
        return this.result;
    }
}

这里重要的部分是方法签名,包括左侧的“< T >”。

protected <T> AbstractListResponse<T> parseAbstractResponse(String json, TypeToken type) {
    return new GsonBuilder()
            .create()
            .fromJson(json, type.getType());
}

调用Gson时,该方法接收泛型对象的TypeToken。

TypeToken<AbstractListResponse<MyDTO>> typeToken = new TypeToken<AbstractListResponse<MyDTO>>() {};
AbstractListResponse<MyDTO> responseBase = parseAbstractResponse(json, typeToken);

最后,TypeToken 可以使用 MyDTO,甚至是一个简单的对象,只是 MyDTO。


4
投票

对于像我一样在 Kotlin 上苦苦挣扎的人,我找到了这种工作方式

val type = object : TypeToken<MyJson<MyObject>>() { }.type
val response = gson.fromJson<MyJson<MyObject>>(reader, type)

请注意,调用泛型函数需要在调用点在函数名称后面添加类型参数(参见 here


3
投票

如果您使用的是gson 2.8.0或更高版本,可以使用以下方法

TypeToken#getParametized((Type rawType, Type... typeArguments))

示例:

protected MyJson<T> doInBackground(Class<T> type, String json, Void... params) {

    GsonBuilder gson = new GsonBuilder();
    Type collectionType = TypeToken.getParameterized(MyJson.class, type).getType();

    MyJson<T> myJson = gson.create().fromJson(json, collectionType);

    System.out.println(myJson.getPosts()); // [true, false]
    return myJson;
}

我相信这对你的情况有用。

归功于这个答案


1
投票

我使用上面的答案来找出 Kotlin 中更通用的方法(但你可以在 Java 中进行细微调整重用) 我有

BaseDB<T>
加载
Table<T>
,而 Table 有
List<T>

fun parse(jsonString: String): Table<T> {
    //this gets the class for the type T
    val classT: Class<*> = (javaClass
        .genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<*>
    val type = TypeToken.getParameterized(Table::class.java, classT).type
    return GsonHelper.gson.fromJson<Table<T>>(jsonString, type)
}

0
投票

在阅读了大量评论后,我找到了一个适合我的解决方案,并且确实由于类型擦除而引发错误。

以下是示例

public <T> T get(String key, Class<T> type)  {
    String json = mPref.getString(key, null);
    return new Gson().fromJson(json, type);
}

mPref 是对象 SharedPref 将其替换为您的对象

你可以像这样调用上面的方法

get("Test", User.class);

希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.