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

Jor*_*uci 13 java android json gson

我正在开发一个使用Gson作为JSON反序列化器的应用程序,需要从REST API反序列化多态JSON.在解释mi问题之前,我已经一直在寻找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;
}
Run Code Online (Sandbox Code Playgroud)

现在这是我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();
}
Run Code Online (Sandbox Code Playgroud)

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

{  
   "user":{  
      "userType":"USER",
      "email":"albert@gmail.com",
      "name":"Albert"
   }
}
Run Code Online (Sandbox Code Playgroud)

我得到这个例外:

com.google.gson.JsonParseException: cannot serialize com.mobile.model.entities.v2.common.User because it already defines a field named userType
Run Code Online (Sandbox Code Playgroud)

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

任何人都可以解释这里发生了什么,为什么不能在pojos层次结构中定义定义类型的属性的名称?

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

Jor*_*uci 7

好吧,经过一段时间的挖掘,我发现问题实际上并没有反序列化,问题来自于序列化并按照问题中的描述注册了RuntimeTypeFactory.如果你注册了一个runtimeTypeAdapterFactory并使用相同的字段名来定义工厂和你的pojo中的类类型,那么使用GSON将pojo序列化为json以及特殊用户的RuntimeTypeAdapterFactory所产生的json将是:

{  
  "user":{  
      "userType":"SPECIAL",
      "email":"albert@gmail.com",
      "name":"Albert"
      "userType":"SPECIAL"
  }
}
Run Code Online (Sandbox Code Playgroud)

这将导致描述的异常:

com.google.gson.JsonParseException: cannot serialize com.mobile.model.entities.v2.common.User because it already defines a field named userType
Run Code Online (Sandbox Code Playgroud)

因为GSON序列化器会在json中重复de field userType,这将自动添加在为类BaseUser注册的RuntimeTypeAdapterFactory中声明的字段.

  • 要解决此问题,请在字段“userType”上使用“transient”修饰符。这将使正常的 GSON 序列化忽略它,但随后 `RuntimeTypeFactory` 像它应该添加的那样添加它。 (2认同)