简体   繁体   English

使用 GSON 反序列化泛型类型时遇到问题

[英]Trouble deserializing generic types using GSON

I have a set of 4 classes which represent my database objects (database is currently only an ArrayList).我有一组 4 个类,它们代表我的数据库对象(数据库目前只是一个 ArrayList)。
I am using GSON to store these objects in a.txt file.我正在使用 GSON 将这些对象存储在 a.txt 文件中。
A single class named Storage is in charge of reading and writing these objects to those.txt files.一个名为Storage的 class 负责将这些对象读取和写入到 those.txt 文件中。
Here is my problem: I have a generic object T extends MyInterface .这是我的问题:我有一个通用的 object T extends MyInterface That object represents the 4 database classes. object 代表 4 个数据库类。 I put T in the Storage method, so I could use just this one class for any database object.我把T放在Storage方法中,所以我可以将这个 class 用于任何数据库 object。 Here is the code:这是代码:

public class Storage<T extends MyInterface> {

     Gson GSON = new Gson();

     public ArrayList<T> readAll() {
         String objectsJSON = TextFileHandler.readFromFile(fileStorageLocation); // This works
         System.out.println(objectsJSON);   // This also works  (prints out correctly)
         Type collectionType = new TypeToken<ArrayList<T>>() {}.getType();  
         return GSON.fromJson(objectsJSON, collectionType);     // This fails
     }

}

I get the following exception:我得到以下异常:

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to 
interfaces.MyInterface

Do you guys have any idea what it could be? 你们知道它可能是什么吗?

The whole point of anonymous TypeToken subtypes is that they're defined at compile time so that generic type information is saved.匿名TypeToken子类型的全部意义在于它们是在编译时定义的,以便保存通用类型信息。 You'll need to do something to make the token information available dynamically.您需要做一些事情来使令牌信息动态可用。 This approach might work: 这种方法可能有效:

private final TypeToken<ArrayList<T>> targetType;

public Storage(Class<T> dataType) {
    targetType = new TypeToken<ArrayList<T>>(){}
        .where(new TypeParameter<T>(){}, dataType);
}

public List<T> readAll() { // avoid using concrete ArrayList in public API;
                           // not a bad idea to change it in the other locations
    ...
    return GSON.fromJson(objectsJson, targetType);
}

You should use interface List , not that implementation as ArrayList您应该使用接口List ,而不是像ArrayList这样的实现

public List<T> readAll() {
     String objectsJSON = TextFileHandler.readFromFile(fileStorageLocation);
     System.out.println(objectsJSON);
     Type collectionType = new TypeToken<List<T>>() {}.getType();  
     return GSON.fromJson(objectsJSON, collectionType);
 }

If that doesn't work, check objectJSON if is correct with JSON format.如果这不起作用,请检查objectJSON是否与 JSON 格式正确。

For anybody who might be looking for an answer to this, here's what I exactly did to fix it:对于任何可能正在寻找答案的人,这就是我为解决它所做的确切工作:

The problem was that GSON did not know which class to instantiate since my Storage<T> was used for a couple of different entities.问题是 GSON 不知道要实例化哪个 class 因为我的Storage<T>用于几个不同的实体。 I had to somehow tell it, to which particular entity class was each instance of Storage<T> used.我不得不以某种方式告诉它,每个Storage<T>实例都使用了哪个特定实体 class。 The error I was getting was something like:我得到的错误是这样的:

Cannot cast LinkedTreeMap to MyInterface.
Because at runtime, the Storage had no idea which class to instantiate.因为在运行时, Storage不知道要实例化哪个 class。

I'm doing a regular Java project, so I downloaded the Guava.jar and added it to my project: http://search.maven.org/remotecontent?filepath=com/google/guava/guava/23.0/guava-23.0.jar I'm doing a regular Java project, so I downloaded the Guava.jar and added it to my project: http://search.maven.org/remotecontent?filepath=com/google/guava/guava/23.0/guava-23.0 .jar

I modified my generic class Storage<T> constructor, to take in the Class<T> dataType parameter so it can know at runtime, which class it's being used for.我修改了我的通用 class Storage<T>构造函数,以接受Class<T> dataType参数,以便它可以在运行时知道它正在用于哪个 class。 I also added a private field TypeToken<List<T>> so it could store information about the class being deserialized.我还添加了一个私有字段TypeToken<List<T>>以便它可以存储有关正在反序列化的 class 的信息。

Essentially, all I did was this:本质上,我所做的只是:

import com.google.common.reflect.TypeParameter;
import com.google.common.reflect.TypeToken;
import com.google.gson.*;

public class Storage<T extends MyInterface> {

     // This was used so the Storage objects knows which dataType it's associated to 
     // Because T is deleted at runtime, so you can't use that.
     private TypeToken<List<T>> targetDataType;

     // I changed the constructor to take in 1 bonus argument
     public Storage(Class<T> dataType) {
          targetDataType = new TypeToken<List<T>>() {}
                        .where(new TypeParameter<T>() {}, dataType);            
     }

     public List<T> deserialize() {
         // Read JSON data into a <jsonString> variable
    
         Gson GSON = new Gson();
         return GSON.fromJson(jsonString, targetDataType.getType());
     }
}

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

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