简体   繁体   English

反序列化时,Gson无法“理解”泛型类型

[英]Gson fails to “understand” generic type when deserializing

I created this memory class: 我创建了这个内存类:

public class Memory {
    private final Hashtable<String, String> data;
    private final Gson gson;

    public Memory() {
        this.data = new Hashtable<String, String>();
        this.gson = new Gson();
    }

    public <T> void set(String key, List<T> value) {
        this.data.put(key, this.gson.toJson(value));
    }

    public <T> List<T> get(String key, Class<T> cls) {
        Type type = new TypeToken<List<T>>() {}.getType();
        return this.gson.fromJson(this.data.get(key), type);
    }
}


Where I can store lists of generic types in a json and then deserialize them. 在哪里可以将通用类型的列表存储在json中,然后反序列化它们。
But when I try to use it, for example like this: 但是,当我尝试使用它时,例如:

public class User {
    private int id;
    private String username;

    public User() { }

    public User(int id, String username) {
        this.id = id;
        this.username = username;
    }
}

Memory memory = new Memory();
List<User> users = new ArrayList<User>();
// add users
memory.set("users", users);

// now get the users back
List<User> copy = memory.get("users", User.class);

Gson returns an ArrayList of StringMap instead of Users. Gson返回StringMap的ArrayList而不是Users。
This obviously has something to do with the generics I'm using, but is there a way to bypass it? 显然这与我使用的泛型有关,但是有没有办法绕过它呢?

Thanks. 谢谢。

The real failure here is highlighted by the obvious inconsistency that Java Generics allow, where a List<User> ends up being filled with instances of com.google.gson.internal.StringMap ! Java泛型允许的明显不一致突出了这里真正的失败,在这种情况下, List<User>最终被com.google.gson.internal.StringMap实例填充! But that's a whole other topic. 但这是另一个主题。

The immediate problem is that you aren't properly using the type token class. 直接的问题是您没有正确使用类型令牌类。 The very point of the token is that you must extend the class with a concrete type - however you are instantiating the class with a method level generic parameter that is validated at compile time then erased (and subsequently unavailable at runtime). 令牌的重点是您必须使用具体的类型扩展类-但是,您正在使用方法级通用参数实例化该类,该方法级通用参数在编译时已验证,然后被擦除 (随后在运行时不可用)。 But the whole point of type tokens is to retain generic information, so the model is blown. 但是类型令牌的全部目的是保留通用信息,因此该模型被淘汰了。

To be honest, this is a failure in implementation of the token - if you compared the constructor code with, for example, the TypeReference implementation of Jackson , you would see that Jackson actually validates that concrete parameters are available. 老实说, TypeReference实现的失败-如果将构造函数代码与例如JacksonTypeReference实现进行比较 ,您会看到Jackson确实验证了可用的具体参数。

31    protected TypeReference()
32    {
33        Type superClass = getClass().getGenericSuperclass();
34        if (superClass instanceof Class<?>) { // sanity check, should never happen
35            throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
36        }
45    }

The easiest solution would be to simply make constructing the type (token) the responsibility of the caller, and pass it in along with the data you wish to store and/or retrieve. 最简单的解决方案是使构造类型(令牌)成为调用者的责任,并将其与您希望存储和/或检索的数据一起传递。

public <T> List<T> get(String key, Type type) {
    return this.gson.fromJson(this.data.get(key), type);
}

public <T> void set(String key, List<T> value, Type type) {
    this.data.put(key, this.gson.toJson(value, type));
}

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

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