Java GSON TypeAdapter:基于“反序列化多态对象”;“类型”;领域

Java GSON TypeAdapter:基于“反序列化多态对象”;“类型”;领域,java,android,gson,retrofit,json-deserialization,Java,Android,Gson,Retrofit,Json Deserialization,我正在对JSON处理使用默认Gson解析器进行改造。通常,我有一系列4~5个相关但略有不同的对象,它们都是公共基的子类型(我们称之为“BaseType”)。我知道我们可以通过检查“type”字段将不同的JSON反序列化到它们各自的子模型。最常用的方法是扩展JsonDeserializer并将其注册为Gson实例中的类型适配器: class BaseTypeDeserializer implements JsonDeserializer<BaseType> { private

我正在对JSON处理使用默认Gson解析器进行改造。通常,我有一系列4~5个相关但略有不同的对象,它们都是公共基的子类型(我们称之为“BaseType”)。我知道我们可以通过检查“type”字段将不同的JSON反序列化到它们各自的子模型。最常用的方法是扩展JsonDeserializer并将其注册为Gson实例中的类型适配器:

class BaseTypeDeserializer implements JsonDeserializer<BaseType> {
    private static final String TYPE_FIELD = "type";

    @Override
    public BaseType deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json.isJsonObject() && json.getAsJsonObject().has(TYPE_FIELD)) {
            JsonObject jsonObject = json.getAsJsonObject();
            final String type = jsonObject.get(TYPE_FIELD).getAsString();
            if ("type_a".equals(type)) {
                return context.deserialize(json, AType.class);
            } else if ("type_b".equals(type)) {
                return context.deserialize(json, BType.class);
            } ...

            // If you need to deserialize as BaseType,
            // deserialize without the current context
            // or you will infinite loop
            return new Gson().fromJson(json, typeOfT);

        } else {
            // Return a blank object on error
            return new BaseType();
        }
    }
}
类BaseTypeDeserializer实现JsonDeserializer{
私有静态最终字符串类型\u FIELD=“TYPE”;
@凌驾
公共基类型反序列化(JsonElement json、类型typeOfT、JsonDeserializationContext上下文)引发JsonParseException{
if(json.isJsonObject()&&json.getAsJsonObject().has(TYPE_字段)){
JsonObject JsonObject=json.getAsJsonObject();
最终字符串类型=jsonObject.get(type_字段).getAsString();
如果(“类型a”。等于(类型)){
反序列化(json,AType.class);
}else if(“type_b.”等于(type)){
返回context.deserialize(json,BType.class);
} ...
//如果需要反序列化为BaseType,
//不使用当前上下文反序列化
//否则你将无限循环
返回新的Gson().fromJson(json,typeOfT);
}否则{
//错误时返回空白对象
返回新的BaseType();
}
}
}
然而,根据我的经验,这是非常缓慢的,而且似乎是因为我们必须将整个JSON文档加载到JsonElement中,然后遍历它以找到类型字段。我也不喜欢这个反序列化程序必须在我们的每一个REST调用上运行,即使数据不一定总是映射到BaseType(或其子项)

这篇文章提到了使用TypeAdapters作为替代方案,但并没有通过一个示例进一步说明

这里有人知道如何使用TypeAdapterFactory基于“type”字段进行反序列化,而不必将整个json流读入JsonElement对象树吗

  • 只有当反序列化数据中有
    BaseType
    或子类时,才应该运行自定义反序列化程序,而不是每个请求。您根据类型注册它,并且仅当gson需要序列化该类型时才会调用它

  • 您是否反序列化了
    BaseType
    以及子类?如果是这样的话,这句话会扼杀你的表现--

    返回新的Gson().fromJson(json,typeOfT)

  • 创建新的
    Gson
    对象并不便宜。每次反序列化基类对象时都会创建一个。将此调用移动到
    BaseTypeDeserializer
    的构造函数并将其存储在成员变量中将提高性能(假设您反序列化了基类)

  • 创建基于字段选择类型的
    TypeAdapter
    TypeAdapterFactory
    的问题在于,在开始使用流之前,您需要知道类型。如果类型字段是对象的一部分,则无法知道该点的类型。你链接到的帖子提到了很多--
  • 使用TypeAdapters编写的反序列化程序可能不如 那些用JSONDESerializer编写的。假设您想要创建一个类型字段 确定对象字段反序列化到的对象。流媒体 API,您需要保证在响应之前 反对

    如果您可以在JSON流中的对象之前获取类型,那么您就可以这样做,否则您的
    TypeAdapter
    实现可能会镜像您当前的实现,但您要做的第一件事是自己转换为JSON树,以便找到类型字段。这不会为您当前的实现节省太多

  • 如果子类相似,并且它们之间没有任何字段冲突(名称相同但类型不同的字段),则可以使用包含所有字段的数据传输对象。使用gson将其反序列化,然后使用它创建对象

    public class MyDTO {
       String type;
       // Fields from BaseType
       String fromBase;
       // Fields from TypeA
       String fromA;
       // Fields from TypeB
       // ...
    }
    
    
    public class BaseType {
      String type;
      String fromBase;
    
      public BaseType(MyDTO dto) {
        type = dto.type;
        fromBase = dto.fromBase;
      }
    }
    
    public class TypeA extends BaseType {
      String fromA;
    
      public TypeA(MyDTO dto) {
        super(dto);
        fromA = dto.fromA;
      }
    }
    
  • 然后,您可以创建一个
    TypeAdapterFactory
    ,处理从DTO到对象的转换--


    #1:Gotcha#2:Yes,有时当子类型本质上与基类型相似(没有唯一字段)时,我会反序列化。我将Gson创建移动到构造函数中(
    static Gson mBaseGson;public BaseType(){mBaseGson=new Gson()}
    并返回mBaseGson.fromJson(json,typeOfT)
    但没有看到任何变化#3:没有选项,因为“type”字段是对象的一部分,所以我没有提前设置它#4:看起来是最可行的选项。但是,我可能会完全放弃多态性,避免理想化——如果我正在创建一个DTOYes,那么当您创建一个包含所有字段的类,你需要看看你的多态性是否真的给你带来了什么。如果你有多态行为,那么它可能值得保留,但它们基本上都是数据对象,没有太多价值
    public class BaseTypeAdapterFactory implements TypeAdapterFactory {
    
      public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
        if(BaseType.class.isAssignableFrom(type.getRawType())) {
          TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
          return newItemAdapter((TypeAdapter<BaseType>) delegate,
              gson.getAdapter(new TypeToken<MyDTO>(){}));
        } else {
          return null;
        }
      }
    
      private TypeAdapter newItemAdapter(
          final TypeAdapter<BaseType> delagateAdapter,
          final TypeAdapter<MyDTO> dtoAdapter) {
        return new TypeAdapter<BaseType>() {
    
          @Override
          public void write(JsonWriter out, BaseType value) throws IOException {
            delagateAdapter.write(out, value);
          }
    
          @Override
          public BaseType read(JsonReader in) throws IOException {
            MyDTO dto = dtoAdapter.read(in);
            if("base".equals(dto.type)) {
              return new BaseType(dto);
            } else if ("type_a".equals(dto.type)) {
              return new TypeA(dto);
            } else {
              return null;
            }
          }
        };
      }
    }
    
    Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(new BaseTypeAdapterFactory())
        .create();
    
    BaseType base = gson.fromJson(baseString, BaseType.class);