使用 GSON 反序列化嵌套的泛型类时出现异常行为

2022-09-03 08:57:51

我正在编写一个类,它将连接到服务器,并根据一些参数检索一个json字符串,该字符串将使用GSON解析为指定的(通过泛型)类。

负责的类的精简版本如下所示:

class Executor<T> {

    private Response<T> response;

    public void execute() {
        Type responseType = new TypeToken<Response<T>>() {}.getType();
        this.response = new Gson().fromJson(json, responseType);
    }

    public Response<T> getResponse() { return this.response; }

}

(-变量如下所示JSON

反序列化后存储数据的类如下所示:

class Response<T> {

    private List<T> data = null;

    public List<T> getData() { return this.data; }

}

数据尝试反序列化的类:

public class Language {
    public String alias;
    public String label;
}

运行的代码利用了上面的类:

Executor<Language> executor = new Executor<Language();
List<Language> languages = executor.execute().getResponse().getData();
System.out.println(languages.get(0).alias); // exception occurs here

这将导致以下异常

ClassCastException: com.google.gson.internal.StringMap 不能 cast to sunnerberg.skolbibliotek.book.Language

任何帮助或建议都非常感谢!


答案 1

简短的回答是,您需要在创建令牌时将 创建的 移出 ,绑定 in (),并将类型标记传递给构造函数。TypeTokenExecutorTResponse<T>new TypeToken<Response<Language>>() {}Executor

长答案是:

类型上的泛型通常在运行时擦除,除非使用泛型参数绑定编译类型。在这种情况下,编译器将泛型类型信息插入到已编译的类中。在其他情况下,这是不可能的。

例如,考虑:

List<Integer> foo = new ArrayList<Integer>();

class IntegerList extends ArrayList<Integer> { ... }
List<Integer> bar = new IntegerList();

在运行时,Java 知道包含整数,因为类型在编译时绑定到,因此泛型类型信息保存在类文件中。但是,的泛型类型信息已被删除,因此在运行时,实际上无法确定它应该包含 s。barIntegerArrayListIntegerListfoofooInteger

因此,经常会出现这样一种情况,即在运行时之前通常会擦除泛型类型信息的情况下,例如在GSON中解析JSON数据的情况下。在这些情况下,我们可以通过使用类型标记来利用这样一个事实,即在编译时绑定类型信息时(如上面的示例所示),类型信息实际上只是方便地存储泛型类型信息的小型匿名类。IntegerList

现在到你的代码:

Type responseType = new TypeToken<Response<T>>() {}.getType();

在类的这一行中,我们创建一个匿名类(继承自 ),该类在编译时具有硬编码(绑定)类型。因此,在运行时,GSON 能够确定您想要 的对象 。但它不知道是什么,因为你没有在编译时指定它!因此,GSON 无法确定它所创建的对象中将包含什么类型,它只是创建一个。ExecutorTypeTokenResponse<T>Response<T>TListResponseStringMap

这个故事的寓意是,你需要在编译时指定它。如果要一般使用,则可能需要在客户端代码中创建该类外部的类型标记。像这样:TExecutor

class Executor<T> {

    private TypeToken<Response<T>> responseType;
    private Response<T> response;

    public Executor(TypeToken<Response<T>> responseType) {
        this.responseType = responseType;
    }

    public void execute() {
        this.response = new Gson().fromJson(json, responseType.getType());
    }

    public Response<T> getResponse() { return this.response; }

}

// client code:
Executor<Language> executor = new Executor<Language>(new TypeToken<Response<Language>>() { });
executor.execute();
List<Language> languages = executor.getResponse().getData();
System.out.println(languages.get(0).alias); // prints "be"

顺便说一句,我确实在我的机器上测试了上述内容。

抱歉,如果太长了!


答案 2

您尚未在此语句之后调用。您无法在此处使用 java 泛型,但可以使用以下代码获得相同的效果。response.execute();Executor<Language> executor = new Executor<Language>();

响应.java

import java.io.Serializable;
import java.util.List;

/**
 *
 * @author visruth
 */
public class Response<T> implements Serializable {

    private List<T> data = null;

    public List<T> getData() {
        return this.data;
    }

    public void setData(List<T> data) {
        this.data = data;
    }

}

语言.java

import java.io.Serializable;

/**
 *
 * @author visruth
 */
public class Language implements Serializable {
    private String alias;
    private String label;

    public String getAlias() {
        return alias;
    }

    public void setAlias(String alias) {
        this.alias = alias;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

}

最后是遗嘱执行人.java

import com.google.gson.Gson;
import java.util.*;

/**
 *
 * @author visruth
 */
public class Executor<T> {
private Response<T> response;

    public Response<T> getResponse() {
        return response;
    }

    /**
     * @param args the command line arguments
     */
    public void executor() {
        //sample data
        Response<Language> response=new Response<Language>();
        Language lan1=new Language();
        lan1.setAlias("alias1");
        lan1.setLabel("label1");
        Language lan2=new Language();
        lan2.setAlias("alias2");
        lan2.setLabel("label2");
        List<Language> listOfLangauges=new ArrayList<Language>();
        listOfLangauges.add(lan1);
        listOfLangauges.add(lan2);
        response.setData(listOfLangauges);
        Gson gson=new Gson();
        String json = gson.toJson(response);

        System.out.println(json);
        Response<Language> jsonResponse = gson.fromJson(json, Response.class);
        List list=jsonResponse.getData();

        List<Language> langs=new ArrayList<Language>();            
        for(int i=0; i<list.size(); i++) {            
        Language lan=gson.fromJson(list.get(i).toString(), Language.class);
        langs.add(lan);
        //System.out.println(lan.getAlias());
        }
        Response<Language> responseMade=new Response<Language>();
        responseMade.setData(langs);
        this.response=(Response<T>) responseMade;

    }
}

您可以按如下方式对其进行测试

Executor<Language> executor = new Executor<Language>();
executor.executor();
List<Language> data = executor.getResponse().getData();
for(Language langu: data) {
    System.out.println(langu.getAlias());
    System.out.println(langu.getLabel());
}