GSON将对象转换为LinkedHashMap而不是我的对象

11

我有这段代码:

public abstract class Repository<Entity extends BaseObject> {

...

public void readFromJson(){
    String content = "JSON content here";
    Gson gson = new Gson();
    Type entityType = new TypeToken<JSONObject<Entity>>(){}.getType();

    jsonObject = gson.fromJson(content, entityType);

    for (Entity ent : jsonObject.getEntities()) ;
    }
}

当我尝试使用foreach循环时,我的实体对象不再是Entity类型,而是LinkedHashMap类型,并且出现以下异常:java.lang.ClassCastException: java.util.LinkedHashMap无法转换为com.tranca.bookstore.domain.shared.BaseObject

这是由我创建的JSONObject类:

public class JSONObject<Entity> {

private List<Entity> entities = new ArrayList<Entity>();
private long lastId = -1;
public List<Entity> getEntities() {
    return entities;
}
public void setEntities(List<Entity> entities) {
    this.entities = entities;
}
public long getLastId() {
    return lastId;
}
public void setLastId(long lastId) {
    this.lastId = lastId;
}

public void incrementLastId() {
    this.lastId++;
}

}

也许基础对象是相关的,所以我会在这里放置代码:

public abstract class BaseObject implements Serializable {

protected long id = (long) -1;
protected int version = 0;

protected BaseObject(){}

public long getId() {
    return id;
}
public void setId(long id) {
    this.id = id;
}
public int getVersion() {
    return version;
}
public void setVersion(int version) {
    this.version = version;
}

}
2个回答

3

我遇到了类似的问题。在稍微不同的情境下,为了给出更明确的答案:

我有一个方法产生了以下错误:“com.google.gson.internal.LinkedTreeMap 无法强制转换为 MyType”:

/**
   * Reads a LinkedHashMap from the specified parcel.
   * 
   * @param <TKey>
   *          The type of the key.
   * @param <TValue>
   *          The type of the value.
   * @param in
   *          The in parcel.
   * @return Returns an instance of linked hash map or null.
   */
  public static <TKey, TValue> LinkedHashMap<TKey, TValue> readLinkedHashMap(Parcel in) {
    Gson gson = JsonHelper.getGsonInstance();
    String content = in.readString();
    LinkedHashMap<TKey, TValue> result = gson.fromJson(content, new TypeToken<LinkedHashMap<TKey, TValue>>(){}.getType());
    return result;
  }

我希望有一种简单通用的方法来读写LinkedHashMap。上述解决方案不起作用,因为我理解的TypeToken与TKey和TValue的类型信息在编译后将会丢失。这是问题所在。如果你将代码更改为以下示例,则会生效,因为现在我们明确定义了类型令牌。我对Java并不是很了解,所以不清楚为什么在这种情况下可以在运行时读取类型信息。

/**
   * Reads a LinkedHashMap from the specified parcel.
   * 
   * @param <TKey>
   *          The type of the key.
   * @param <TValue>
   *          The type of the value.
   * @param in
   *          The in parcel.
   * @return Returns an instance of linked hash map or null.
   */
  public static <TKey, TValue> LinkedHashMap<TKey, TValue> readLinkedHashMap(Parcel in, TypeToken<LinkedHashMap<TKey, TValue>> typeToken) {
    Gson gson = JsonHelper.getGsonInstance();
    Type type = typeToken.getType();
    String content = in.readString();
    LinkedHashMap<TKey, TValue> result = gson.fromJson(content, type);
    return result;
  }

现在您可以像这样调用上述功能:

readLinkedHashMap(in, new TypeToken<LinkedHashMap<UUID, MyObject>>(){});

一则旁注1:在编写链接哈希映射时,您根本不需要指定任何类型令牌。toJson(map)就足够了。

一则旁注2(针对我遇到的问题):默认情况下,gson使用toString()来序列化键。如果您为键类型注册了类型适配器,该类型可能是更复杂的类型,则在序列化时不会应用此类型适配器,但在反序列化时将应用该类型适配器。这导致过程不一致,因此失败。以下选项激活复杂映射键序列化

gsonBuilder.enableComplexMapKeySerialization()

1
主要是 gsonBuilder.enableComplexMapKeySerialization() 解决了我的问题。 - CrandellWS

2

终于搞定了!

问题在于:

new TypeToken< JSONObject< Entity>>(){}.getType();

返回的是JSONObject< T>的类型,而不是扩展Repository(例如UserRepository扩展Repository< User>)的子类的具体实体。

诀窍是创建一个抽象方法来强制子类设置反序列化的类型。

总之,如果你遇到这个错误,请确保你使用的是正确的类类型(如果你使用子类,请确保它返回的是你的子类类型而不是父类类型)。


6
抱歉,我遇到了同样的问题,但我不理解你的答案,你可以请说明一下吗?谢谢! - Jonathan Leung
1
@JonathanLeung:如果我理解正确,问题在于每次执行表达式new TypeToken<JSONObject<Entity>>(){}时都会实例化同一个匿名类的实例。 Entity并不是真正的类,它只是Repository的类型参数,因此在这一点上完全被擦除,甚至无法通过反射进行处理。(TypeToken使用反射来检查类层次结构以绕过擦除,但当类本身引用类型参数时,它不起作用。) - ruakh
1
抱歉,你的回答依然不清晰。如果您能提供一个有意义的解决方案就好了。 - Ajith M A
@ajith 他提供了一个有意义的解决方案。他的意思是在您的具体类中实现一个返回类类型的方法(例如,返回User.class),因为T只是一个类型参数,而不是类类型,这正是gson正在寻找的。 - Maica Ballangan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接