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反序列化到实际对象时发现了一些问题。