用Gson抛出异常反序列化多态JSON



我正在研究一个使用Gson作为JSON反序列化器的应用程序,需要从REST API反序列化多态JSON。在解释我的问题之前,请注意,我已经用Gson研究了多态反序列化,并成功地在几个案例中实现了它。所以这是我遇到的一个特殊问题。在问这个问题之前,我也读了这篇很棒的文章和关于Stack Overflow的讨论。我使用RuntimeTypeAdapterFactory来反序列化多态对象。

问题我所拥有的是,显然GSON的RuntimeTypeAdapterFactory不允许声明指定层次结构内部对象类型的字段。我将用一些代码进一步解释。我有以下pojos结构(为了简单起见,pojos已经减少了):

public abstract class BaseUser {
    @Expose
    protected EnumMobileUserType userType; 
}

public class User extends BaseUser {
    @Expose
    private String name;
    @Expose
    private String email;     
}
public class RegularUser extends User {
    @Expose
    private String address;    
}
public class SpecialUser extends User {
    @Expose
    private String promoCode;
}

现在这是我为用户层次结构定义RuntimeTypeAdapterFactory的代码。

public static RuntimeTypeAdapterFactory<BaseUser> getUserTypeAdapter() {
   return RuntimeTypeAdapterFactory
        .of(BaseUser.class, "userType")
        .registerSubtype(User.class, EnumMobileUserType.USER.toString())
        .registerSubtype(RegularUser.class, EnumMobileUserType.REGULAR.toString())
        .registerSubtype(SpecialUser.class, EnumMobileUserType.SPECIAL.toString());
}
public static Gson getGsonWithTypeAdapters() {
    GsonBuilder builder = new GsonBuilder();
    builder.registerTypeAdapterFactory(getUserTypeAdapter());
    return builder.create();
}

现在当我尝试反序列化JSON文本时:

{  
   "user":{  
      "userType":"USER",
      "email":"albert@gmail.com",
      "name":"Albert"
   }
}

我得到了这个异常:

com.google.gson.JsonParseException: cannot serialize com.mobile.model.entities.v2.common.User because it already defines a field named userType

但是,如果我在我的BaseUser类更改属性"userType"的名称为"类型",例如,我反序列化相同的JSON,一切正常工作。我不明白为什么Gson RuntimeTypeAdapterFactory有这个限制。事实上,在这篇博文中,这显然不是一个问题。

谁能解释一下这里发生了什么,为什么定义类型的属性名不能在pojos层次结构中定义?

EDIT问题不在于反序列化时,而在于使用上述代码进行序列化时。在答案中找到进一步的解释。

嗯,经过一段时间的挖掘,我发现问题实际上不是反序列化,问题出现在序列化和注册RuntimeTypeFactory时,如问题中所述。如果你注册了一个runtimeTypeAdapterFactory,并使用相同的字段名来定义工厂和pojo中的类类型,那么使用GSON将pojo序列化为json的结果将是:

{  
  "user":{  
      "userType":"SPECIAL",
      "email":"albert@gmail.com",
      "name":"Albert"
      "userType":"SPECIAL"
  }
}

这将导致如下所述的异常:

com.google.gson.JsonParseException: cannot serialize com.mobile.model.entities.v2.common.User because it already defines a field named userType

因为字段userType在json中是重复的,由于GSON序列化器,它将自动添加一个字段,如在为类BaseUser注册的RuntimeTypeAdapterFactory中声明的。

我认为使用您自己的userType而不使用@Expose注释将会达到目的

Regads

你总是可以使用默认的Gson实例来序列化 (new Gson()),然后使用RuntimeTypeAdapterFactory实例来反序列化

如果你想转换所有内容,建议不要使用@Expose。它只会让你的模型类带有多余的注释。

我在android/kotlin上有同样的问题,JorgeMuci的答案对我不起作用,但是使用同样的方法,我使用@Transient标签,它工作得很好

最新更新