【问题标题】:Deserializing polymorphic JSON with Gson throwing exception使用 Gson 引发异常反序列化多态 JSON
【发布时间】:2017-02-07 11:55:39
【问题描述】:

我正在开发一个使用 Gson 作为 JSON 反序列化器的应用程序,并且需要从 REST API 反序列化多态 JSON。在解释 mi 问题之前,请注意我已经在研究 Gson 的多态反序列化,并在几个案例中成功实现了它。所以这是我遇到的一个具体问题。在问这个问题之前,我也读过this great postthis Stack Overflow discussion。顺便说一句,我正在使用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”的名称更改为“type”,并且反序列化相同的 JSON,则一切正常。我不明白为什么 Gson RuntimeTypeAdapterFactory 有这个限制。事实上,this blog post 显然这不是问题。

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

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

【问题讨论】:

  • 根据错误,您确定您还没有在User 类中声明字段userType 吗?它已在“BaseUser”类中声明,因此无需重新声明。
  • 嗨乔特曼。不,我确定我没有两次声明字段用户类型。它仅在基本用户上声明。另外,我在问题结束时说过,只要将字段 userType 的名称从 BaseUser 类更改为与 json 和 RuntimeTypeAdapterFactory 上声明的名称不同的名称,反序列化就可以正常工作。不过谢谢你的建议!

标签: java android json gson


【解决方案1】:

好吧,经过一段时间的挖掘,我发现问题实际上并不是反序列化,而是在序列化并按照问题中的描述注册 RuntimeTypeFactory 时出现问题。如果您注册一个 runtimeTypeAdapterFactory 并使用相同的字段名称来定义工厂和 pojo 中的类类型,则使用 GSON 和 RuntimeTypeAdapterFactory 将 pojo 序列化为 json 生成的 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

因为 GSON 序列化程序在 json 中重复了 de field userType,它会自动添加一个字段,如在为类 BaseUser 注册的 RuntimeTypeAdapterFactory 中声明的。

【讨论】:

  • 要解决此问题,请在字段 userType 上使用 transient 修饰符。这将使正常的 GSON 序列化忽略它,但随后 RuntimeTypeFactory 会按原意添加它。
【解决方案2】:

我认为使用您自己的 userType 而不使用 @Expose 注释可以解决问题

问候

【讨论】:

    【解决方案3】:

    您始终可以使用默认的 Gson 实例来序列化(new Gson()),然后使用 RuntimeTypeAdapterFactory 实例来反序列化

    如果您希望对所有内容进行转换,建议不要使用@Expose。它只会用多余的注释破坏你的模型类。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多