Java Gson反序列化泛型类型适配器的基类
我有以下课程:Java Gson反序列化泛型类型适配器的基类,java,json,gson,Java,Json,Gson,我有以下课程: public class Kit { private String name; private int num; } 我有一个类,它扩展了Kit的附加功能: public class ExtendedKit extends Kit { private String extraProperty; } 使用Gson,我希望能够反序列化这两个类以及更多不同的类型,而无需为它们创建一堆类型适配器,因为它们都具有相同的Json结构: { "type":
public class Kit {
private String name;
private int num;
}
我有一个类,它扩展了Kit的附加功能:
public class ExtendedKit extends Kit {
private String extraProperty;
}
使用Gson,我希望能够反序列化这两个类以及更多不同的类型,而无需为它们创建一堆类型适配器,因为它们都具有相同的Json结构:
{
"type": "com.driima.test.ExtendedKit",
"properties": {
"name": "An Extended Kit",
"num": 124,
"extra_property": "An extra property"
}
}
它被传递到注册到my GsonBuilder的以下类型适配器中:
public class GenericAdapter<T> implements JsonDeserializer<T> {
@Override
public T deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
final JsonObject object = json.getAsJsonObject();
String classType = object.get("type").getAsString();
JsonElement element = object.get("properties");
try {
return context.deserialize(element, Class.forName(classType));
} catch (ClassNotFoundException e) {
throw new JsonParseException("Unknown element type: " + type, e);
}
}
}
注意:添加了PostProcessExecutor,这样我就可以对反序列化的任何可以进行后处理的对象应用后处理。有一篇文章帮助我实现了这一功能。我认为
JsonDeserializer
不是一个好的选择:
- 您需要将每个类型绑定到易出错的
中的GsonBuilder
实例,或者使用Gson
registerTypeHierarchyAdapter
- 对于后者,您将遇到无限递归(如果我没有错的话:因为上下文只提供了一种机制来反序列化相同类型的实例)
最终类多态性TypeAdapterFactory
实现TypeAdapterFactory{
//让我们不要在这里硬编码'Kit.class',让用户在呼叫站点获取类型
私有最终谓词>谓词;
私有多态类型适配器工厂(最终谓词>谓词){
this.predicate=谓词;
}
静态TypeAdapterFactory获取(最终谓词>谓词){
返回新的多态类型适配器工厂(谓词);
}
@凌驾
公共类型适配器创建(最终Gson Gson、最终TypeToken TypeToken){
最后一个类我认为适配器中的一个问题是,ExtendedKit
实际上从来没有调用它,而只调用Kit
。因此,我想这就是为什么它与ExtendedKit
一起工作的原因。由于类型擦除,Gson默认情况下无法处理泛型
尽管如此:声明要反序列化的对象是一种很好且明确的做法,就像Json所表示的那样,因为它通常会减少适配器等的逻辑编码
我建议您为Kit
声明一个包装类,比如:
@Getter
@AllArgsConstructor
public class KitWrapper {
private String type;
@SerializedName("properties") //
private Kit kit;
}
@Slf4j
public class KitWrapperAdapter implements JsonDeserializer<KitWrapper> {
@Override
public KitWrapper deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
try {
@SuppressWarnings("unchecked")
Class<? extends Kit> classKit =
(Class<? extends Kit>)Class.forName(json.getAsJsonObject()
.get("type").getAsString() );
JsonElement jeProperties = json.getAsJsonObject().get("properties");
Kit kit = context.deserialize(jeProperties, classKit);
// Not needed to parse anymore, new KitWrapper can be created
// with this information
return new KitWrapper(classKit.getName(), kit);
} catch (Exception e) {
log.error("{}", e.toString());
return null;
}
}
}
使用TypeAdapter
进行反序列化比较容易,例如:
@Getter
@AllArgsConstructor
public class KitWrapper {
private String type;
@SerializedName("properties") //
private Kit kit;
}
@Slf4j
public class KitWrapperAdapter implements JsonDeserializer<KitWrapper> {
@Override
public KitWrapper deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
try {
@SuppressWarnings("unchecked")
Class<? extends Kit> classKit =
(Class<? extends Kit>)Class.forName(json.getAsJsonObject()
.get("type").getAsString() );
JsonElement jeProperties = json.getAsJsonObject().get("properties");
Kit kit = context.deserialize(jeProperties, classKit);
// Not needed to parse anymore, new KitWrapper can be created
// with this information
return new KitWrapper(classKit.getName(), kit);
} catch (Exception e) {
log.error("{}", e.toString());
return null;
}
}
}
正如前面提到的那样,您的案例几乎适用于RunTimeAdapterFactory。看起来,类型应该是反序列化对象的属性,而不是使其成为顶级属性,而实际对象是较低级别。换句话说,如果您可以更改Json&工具包
因此:
{
"type": "com.driima.test.ExtendedKit",
"name": "An Extended Kit",
"num": 124,
"extra_property": "An extra property"
}
它可能工作得很好。在这种情况下,您可能对(甚至不是Maven或Gradle用户)感兴趣
但如果不能,我建议使用包装类
但是,正如您所看到的,自己编写代码也没什么大不了的。可以做到这一点,但如果我只想反序列化一个没有extraProperty的工具包
,这就不起作用了。这意味着您给出的Json没有额外属性,并且具有“type”:“com.driima.test.Kit”
?是的。这是正确的。很抱歉没有在我的问题中澄清这一点。我正在使用这种类型。这非常好。这正是我想要的,我仍然可以使用我的后处理执行器。不过,我正在尝试解决一个小问题。我有两种后处理-第一种只是使用空后处理()进行后处理方法。如果我不需要任何附加信息,这很好,但在某些情况下,我不想临时存储json中的数据,并希望对其求值,因此我有另一个后处理方法,该方法从json传递“properties”对象。有没有办法获得相同的功能?postProcess(JsonObject属性);
没关系-我刚刚创建了一个额外的类型适配器工厂,可以处理这个问题。再次感谢您的帮助!我在将生成的json反序列化到实际对象时发现了一些问题。