kip*_*ip2 5 java android gson json-deserialization retrofit
我正在使用带有默认 Gson 解析器的 Retrofit 进行 JSON 处理。通常,我有一系列 4~5 个相关但略有不同的对象,它们都是公共基础的所有子类型(我们称之为“BaseType”)。我知道我们可以通过检查“类型”字段将不同的 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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
然而,根据我的经验,这真的很慢,似乎是因为我们必须将整个 JSON 文档加载到 JsonElement 中,然后遍历它以找到类型字段。我也不喜欢这个反序列化器必须在我们的每个 REST 调用上运行,即使数据不一定总是映射到 BaseType(或其子项)。
这篇foursquare 博客文章提到使用TypeAdapters 作为替代方法,但它并没有真正通过示例进行进一步说明。
这里有人知道如何使用 TypeAdapterFactory 基于“类型”字段进行反序列化,而不必将整个 json 流读入 JsonElement 对象树?
自定义反序列化器应该只BaseType在反序列化数据中有 a或 a 子类时才运行,而不是每个请求。您根据类型注册它,并且仅在 gson 需要序列化该类型时才调用它。
你BaseType像子类一样反序列化吗?如果是这样,这条线会扼杀你的表现——
return new Gson().fromJson(json, typeOfT);
创建新Gson对象并不便宜。每次反序列化基类对象时都会创建一个。将此调用移动到 的构造函数BaseTypeDeserializer并将其存储在成员变量中将提高性能(假设您确实反序列化了基类)。
TypeAdapter或TypeAdapterFactory选择类型的问题是您需要在开始使用流之前知道类型。如果类型字段是对象的一部分,则此时您无法知道类型。你链接到的帖子提到了很多——使用 TypeAdapters 编写的反序列化器可能不如使用 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;
}
}
Run Code Online (Sandbox Code Playgroud)然后你可以创建一个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;
}
}
};
}
}
Run Code Online (Sandbox Code Playgroud)
像这样使用——
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new BaseTypeAdapterFactory())
.create();
BaseType base = gson.fromJson(baseString, BaseType.class);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3746 次 |
| 最近记录: |