我正在使用Retrofit和默认的Gson解析器进行JSON处理。通常,我有一系列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();
}
}
}
但是,根据我的经验,这非常慢,似乎是因为我们必须将整个JSON文档加载到JsonElement中,然后遍历它以查找类型字段。我也不喜欢这个反序列化器必须在我们的每个REST调用上运行,即使数据并不总是必须映射到BaseType(或其子类)。
这个foursquare blog post提到使用TypeAdapters作为替代方案,但它并没有真正进一步的例子。
这里的任何人都知道如何使用TypeAdapterFactory基于'type'字段反序列化,而不必将整个json流读入JsonElement对象树?
答案 0 :(得分:3)
只有在反序列化数据中有BaseType
或子类时,才应运行自定义反序列化程序,而不是每个请求。您可以根据类型进行注册,只有在gson需要序列化该类型时才会调用它。
您是否反序列化BaseType
以及子类?如果是这样,这条线将会破坏你的表现 -
return new Gson().fromJson(json, typeOfT);
创建新的Gson
对象并不便宜。每次反序列化基类对象时都要创建一个。将此调用移动到BaseTypeDeserializer
的构造函数并将其存储在成员变量中将提高性能(假设您对基类进行反序列化)。
TypeAdapter
或TypeAdapterFactory
的问题是,在开始使用流之前,您需要知道类型。如果类型字段是对象的一部分,则您无法知道该点的类型。你链接的帖子提到了 - 使用TypeAdapter编写的反序列化程序可能不如 那些用JsonDeserializers编写的。想象一下,你想要一个类型字段 确定对象字段反序列化的内容。随着流媒体 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到您的对象的转换 -
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);