Trouble deserializing generic types using GSON.

huangapple go评论69阅读模式
英文:

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将这些对象存储在.txt文件中。

A single class named Storage is in charge of reading and writing these objects to those .txt files.

一个名为Storage的单一类负责将这些对象读取并写入那些.txt文件。

Here is my problem:

这是我的问题:

I have a generic object T extends MyInterface. That object represents the 4 database classes.

我有一个泛型对象T extends MyInterface。该对象代表了这4个数据库类。

I put T in the Storage method, so I could use just this one class for any database object.

我将T放入了Storage方法中,这样我就可以仅使用这个类来处理任何数据库对象。

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?

你们有任何想法是什么导致了这个问题吗?

英文:

I have a set of 4 classes which represent my database objects (database is currently only an ArrayList).
<br>I am using GSON to store these objects in a .txt file.
<br>A single class named Storage is in charge of reading and writing these objects to those .txt files.
<br> Here is my problem:
I have a generic object T extends MyInterface. That object represents the 4 database classes.
I put T in the Storage method, so I could use just this one class for any database object.
Here is the code:

public class Storage&lt;T extends MyInterface&gt; {

     Gson GSON = new Gson();

     public ArrayList&lt;T&gt; readAll() {
	     String objectsJSON = TextFileHandler.readFromFile(fileStorageLocation); // This works
	     System.out.println(objectsJSON);	// This also works  (prints out correctly)
	     Type collectionType = new TypeToken&lt;ArrayList&lt;T&gt;&gt;() {}.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

<br>
Do you guys have any idea what it could be?

答案1

得分: 2

以下是翻译好的部分:

匿名的TypeToken子类型的整个目的是它们在编译时定义,以便保存泛型类型信息。您需要执行某些操作以在运行时使令牌信息可用。这种方法可能有效:

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() { // 避免在公共API中使用具体的ArrayList;
                           // 在其他位置更改它并不是一个坏主意
    ...
    return GSON.fromJson(objectsJson, targetType);
}
英文:

The whole point of anonymous TypeToken subtypes is that they're defined at compile time so that generic type information is saved. You'll need to do something to make the token information available dynamically. This approach might work:

private final TypeToken&lt;ArrayList&lt;T&gt;&gt; targetType;

public Storage(Class&lt;T&gt; dataType) {
    targetType = new TypeToken&lt;ArrayList&lt;T&gt;&gt;(){}
        .where(new TypeParameter&lt;T&gt;(){}, dataType);
}

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

</details>



# 答案2
**得分**: 0

请使用接口 `List`,而不是 `ArrayList` 作为实现。

```java
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);
 }
 
如果这不起作用,请检查 `objectsJSON` 是否符合 JSON 格式。
英文:

You should use interface List, not that implementation as ArrayList

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

If that doesn't work, check objectJSON if is correct with JSON format.

答案3

得分: 0

以下是翻译好的部分:

问题是GSON不知道要实例化哪个类,因为我的Storage&lt;T&gt;用于多个不同的实体。我不得不以某种方式告诉它,每个Storage&lt;T&gt;实例用于哪个特定的实体类。我得到的错误信息类似于:
无法将LinkedTreeMap转换为MyInterface。
因为在运行时,Storage不知道要实例化哪个类。

我正在进行一个普通的Java项目,所以我下载了Guava.jar并将其添加到我的项目中:
Guava下载链接

我修改了我的通用类Storage&lt;T&gt;的构造函数,以接受Class&lt;T&gt; dataType参数,以便它可以在运行时知道它正在用于哪个类。我还添加了一个私有字段TypeToken&lt;List&lt;T&gt;&gt;,以便它可以存储有关正在反序列化的类的信息。

基本上,我所做的就是这样:

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

public class Storage&lt;T extends MyInterface&gt; {

    // 这是为了让Storage对象知道它与哪种dataType相关联
    // 因为T在运行时被删除,所以不能使用它。
    private TypeToken&lt;List&lt;T&gt;&gt; targetDataType;

    // 我改变了构造函数以接受1个额外参数
    public Storage(Class&lt;T&gt; dataType) {
        targetDataType = new TypeToken&lt;List&lt;T&gt;&gt;() {}
            .where(new TypeParameter&lt;T&gt;() {}, dataType);			
    }

    public List&lt;T&gt; deserialize() {
        // 读取JSON数据到一个&lt;jsonString&gt;变量

        Gson GSON = new Gson();
        return GSON.fromJson(jsonString, targetDataType.getType());
    }
}

注意:由于代码中包含HTML编码的符号(如<和>),它们在翻译中保持不变。

英文:

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&lt;T&gt; was used for a couple of different entities. I had to somehow tell it, to which particular entity class was each instance of Storage&lt;T&gt; used.
The error I was getting was something like:
<br><br>
Cannot cast LinkedTreeMap to MyInterface.
<br>
Because at runtime, the Storage had no idea which class to instantiate.

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&lt;T&gt; constructor, to take in the Class&lt;T&gt; dataType parameter so it can know at runtime, which class it's being used for.
I also added a private field TypeToken&lt;List&lt;T&gt;&gt; so it could store information about the class being deserialized.

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&lt;T extends MyInterface&gt; {

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

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

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

huangapple
  • 本文由 发表于 2020年8月13日 03:30:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/63383526.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定