简体   繁体   English

使用 GSON 反序列化泛型类型

[英]Deserializing Generic Types with GSON

I have some problems with implementation of Json Deserialization in my Android application (with Gson library)我在我的 Android 应用程序中实现 Json 反序列化有一些问题(使用 Gson 库)

I've made class like this我上过这样的课

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

And Deserialization call is:反序列化调用是:

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);
  ...
  }
}

Problem is that result.posts list after call holds one Array of LinkedTreeMap Objects(with correct values so problem is Deserialization) instead of MyJson Objects.问题是调用后的 result.posts 列表包含一个 LinkedTreeMap 对象数组(具有正确的值,因此问题是反序列化)而不是 MyJson 对象。 When I use MyObject instead of T everything is running fine and MyObject is correct.当我使用 MyObject 而不是 T 时,一切都运行良好并且 MyObject 是正确的。

So is there any way to implement deserialization call without creating custom deserializer?那么有没有什么方法可以在不创建自定义反序列化器的情况下实现反序列化调用呢?

You have to specify the type of T at the time of deserialization.您必须在反序列化时指定T的类型。 How would your List of posts get created if Gson didn't know what Type to instantiate?如果Gson不知道要实例化的Type ,您的posts List将如何创建? It can't stay T forever.它不能永远停留在T上。 So, you would provide the type T as a Class parameter.因此,您将提供类型T作为Class参数。

Now assuming, the type of posts was String you would deserialize MyJson<String> as (I've also added a String json parameter for simplicity; you would read from your reader as before):现在假设, posts的类型是String您将反序列化MyJson<String>为(为了简单起见,我还添加了一个String json参数;您将像以前一样从reader那里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;
}

Similarly, to deserialize a MyJson of Boolean objects类似地,反序列化Boolean对象的MyJson

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;
}

I've assumed MyJson<T> for my examples to be as我假设MyJson<T>作为我的例子

public class MyJson<T> {

    public List<T> posts;

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

So, if you were looking for to deserialize a List<MyObject> you would invoke the method as因此,如果您正在寻找反序列化List<MyObject>您将调用该方法

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

Have you tried?你有没有尝试过?

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

EDIT编辑

After reading this post it seems that you use of Type is correct.阅读这篇文章后,您似乎使用Type是正确的。 I believe your issue is the use of T .我相信您的问题是T的使用。 You must remember that with Java there is type-erasure.您必须记住,在 Java 中存在类型擦除。 This means that at runtime all instances of T are replaced with Object .这意味着在运行时T所有实例都替换为Object Therefore at runtime what you are passing GSON is really MyJson<Object> .因此,在运行时传递 GSON 的实际上是MyJson<Object> If you tried this with a concrete class in place of <T> I believe it would work.如果你用一个具体的类代替<T>尝试这个,我相信它会起作用。

Google Gson - deserialize list<class> object? Google Gson - 反序列化 list<class> 对象? (generic type) (通用类型)

So the above answer didn't work for me, after trial and error that's how my code ended:所以上面的答案对我不起作用,经过反复试验,我的代码就是这样结束的:

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

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

The important part here is the method signature, including the '< T >' on the left.这里的重要部分是方法签名,包括左侧的“< T >”。

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

When calling Gson, the method receives the TypeToken of the generic object.调用 Gson 时,该方法接收泛型对象的 TypeToken。

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

And finally the TypeToken can use MyDTO, or even a simple object, just MyDTO.最后,TypeToken 可以使用 MyDTO,甚至是一个简单的对象,只是 MyDTO。

For anyone struggling with Kotlin like I did, I've found this way to work对于像我一样在 Kotlin 上挣扎的人,我找到了这种工作方式

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

Note that calling a generic function requires the type arguments at the call site after the name of the function (seen here )请注意,调用泛型函数需要在函数名称之后的调用站点上使用类型参数(参见此处

If you are using gson 2.8.0 or higher, you can use the following method TypeToken#getParametized((Type rawType, Type... typeArguments))如果您使用的是 gson 2.8.0 或更高版本,则可以使用以下方法TypeToken#getParametized((Type rawType, Type... typeArguments))

Example:例子:

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;
}

I believe this would work in your case.我相信这适用于您的情况。

Credits to this answer .归功于此答案

I used the above answer to figure out more generic way in Kotlin (but you can reuse in java with minor adjustment) I have BaseDB<T> which loads Table<T> , while Table has a List<T>我使用上面的答案在 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)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM