我正在编写一个将连接到服务器并基于某些参数的类,检索一个json-string,该字符串将使用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
- 变量looks like this。)
一旦反序列化存储数据的类如下所示:
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无法强制转换为sunnerberg.skolbibliotek.book.Language
非常感谢任何帮助或建议!
答案 0 :(得分:29)
简短的回答是,您需要将TypeToken
的创建移出Executor
,在创建令牌时绑定T
Response<T>
new TypeToken<Response<Language>>() {}
Executor
1}}),并将类型标记传递给List<Integer> foo = new ArrayList<Integer>();
class IntegerList extends ArrayList<Integer> { ... }
List<Integer> bar = new IntegerList();
构造函数。
答案很长:
类型上的泛型通常在运行时被擦除,除非类型是编译且带有泛型参数绑定。在这种情况下,编译器将泛型类型信息插入到已编译的类中。在其他情况下,这是不可能的。
例如,考虑一下:
bar
在运行时,Java 知道 Integer
包含整数,因为ArrayList
类型在编译时绑定到IntegerList
,因此通用类型信息保存在foo
类文件。但是,foo
的泛型类型信息已被删除,因此在运行时无法确定Integer
是否应包含IntegerList
。
因此,经常出现的情况是,在运行时通常会擦除通用类型信息,例如在GSON中解析JSON数据的情况。在这些情况下,我们可以利用以下事实:类型信息在编译时被绑定(如上面的Type responseType = new TypeToken<Response<T>>() {}.getType();
示例中),使用type tokens,这实际上只是一个小小的匿名方便存储泛型类型信息的类。
现在你的代码:
Executor
在TypeToken
类的这一行中,我们创建了一个匿名类(继承自Response<T>
),它在编译时具有类型Response<T>
硬编码(绑定)。因此,在运行时,GSON能够确定您想要一个T
的对象。但它不知道List
是什么,因为你没有在编译时指定它!因此,GSON无法确定其创建的Response
对象的StringMap
中的类型,而只是创建了T
。
故事的寓意是你需要在编译时指定Executor
。如果一般使用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"
,则可能需要在客户端代码中在该类之外创建类型标记。类似的东西:
{{1}}
顺便说一下,我在我的机器上测试了上面的内容。
很抱歉,如果那太长了!
答案 1 :(得分:2)
在response.execute();
此声明之后,您尚未调用Executor<Language> executor = new Executor<Language>();
。你不能在这里使用java泛型,但你可以使用以下代码获得相同的效果。
Response.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;
}
}
Language.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;
}
}
最后,Executor.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());
}
答案 2 :(得分:0)
您的问题与java Type擦除有关。泛型只在编译时才知道。
很遗憾,GSON无法通过反射知道它应该反序列化哪个类。