Java Gson-基于字段值反序列化到特定对象类型

Java Gson-基于字段值反序列化到特定对象类型,java,json,deserialization,gson,Java,Json,Deserialization,Gson,我想根据type字段值将json对象反序列化为特定类型的对象(使用Gson库),例如: [ { "type": "type1", "id": "131481204101", "url": "http://something.com", "name": "BLAH BLAH", "icon": "SOME_STRING", "price": "FREE",

我想根据
type
字段值将json对象反序列化为特定类型的对象(使用Gson库),例如:

[
    {
          "type": "type1",
          "id": "131481204101",
          "url": "http://something.com",
          "name": "BLAH BLAH",
          "icon": "SOME_STRING",
          "price": "FREE",
          "backgroundUrl": "SOME_STRING"
    },
    {
        ....
    }
]
因此,
type
字段将具有不同(但已知)的值。基于该值,我需要将json对象反序列化为适当的模型对象,例如:Type1Model、Type2Model等。
我知道,在反序列化之前,我可以通过将它转换为
JSONArray
,迭代它,并确定它应该反序列化为哪种类型,从而轻松地做到这一点。但我认为这是一个丑陋的方法,我正在寻找更好的方法。有什么建议吗?

您可以实现一个
JsonDeserializer
,并在将Json值解析为Java实例时使用它。我将试着用一段代码来展示它,这将给你一个想法:

1) 定义自定义的
JsonDeserializer
类,该类通过传入json值的id属性创建不同的类实例:

class MyTypeModelDeserializer implements JsonDeserializer<MyBaseTypeModel> {

    @Override
    public MyBaseTypeModel deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
            throws JsonParseException {

        JsonObject jsonObject = json.getAsJsonObject();

        JsonElement jsonType = jsonObject.get("type");
        String type = jsonType.getAsString();

        MyBaseTypeModel typeModel = null;     

        if("type1".equals(type)) {
            typeModel = new Type1Model();
        } else if("type2".equals(type)) {
            typeModel = new Type2Model();
        }
        // TODO : set properties of type model

        return typeModel;
    }
}
3) 定义扩展基类的java对象类的不同实例:

class Type1Model extends MyBaseTypeModel {
    // TODO: add specific fields for this class
}

class Type2Model extends MyBaseTypeModel {
    // TODO: add specific fields for this class
}
String jsonString = ...
BaseClass baseInstance = gson.fromJson(jsonString, BaseClass.class);
4) 将json值解析为bean时使用以下类:

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MyBaseTypeModel.class, new MyTypeModelDeserializer());
Gson gson = gsonBuilder.create();

MyBaseTypeModel myTypeModel = gson.fromJson(myJsonString, MyBaseTypeModel.class);
我现在不能测试它,但我希望你能明白。也会很有帮助。

使用

然后用

public static final class JsonAdapterFactory extends 
    RuntimeTypeAdapterFactory<MediumSummaryInfo> {
        public JsonAdapterFactory() {
            super(MyBaseType.class, "type");
            registerSubtype(MySubtype1.class, "type1");
            registerSubtype(MySubtype2.class, "type2");
        }
}
公共静态最终类JsonAdapterFactory扩展
RuntimeTypeAdapterFactory{
公共JsonAdapterFactory(){
super(MyBaseType.class,“type”);
registerSubtype(MySubtype1.class,“type1”);
registerSubtype(MySubtype2.class,“类型2”);
}
}
并添加注释:

@JsonAdapter(MyBaseType.JsonAdapterFactory.class)

到MyBaseType


好多了。

@stephane-k的答案很有效,但有点混乱,可以改进(见对他的答案的评论)

复制到您的项目中。(没关系;这些类是为复制/粘贴而设计的)

设置模型继承:

// abstract is optional
abstract class BaseClass {
}

class Type1Model extends BaseClass {
}

class Type2Model extends BaseClass {
}
设置GSON或更新现有GSON:

RuntimeTypeAdapterFactory<BaseClass> typeAdapterFactory = RuntimeTypeAdapterFactory
        .of(BaseClass.class, "type")
        .registerSubtype(Type1Model.class, "type1")
        .registerSubtype(Type2Model.class, "type2");

Gson gson = new GsonBuilder().registerTypeAdapterFactory(typeAdapterFactory)
                .create();
baseInstance
将是
Type1Model
Type2Model
的实例


从这里,您可以对接口进行编码或检查instanceof and cast。

如果您有很多子类型,并且您不想或无法维护它们的列表,您还可以使用基于注释的方法

以下是必需的代码和一些使用示例: (它是Kotlin,但可以很容易地移植到Java)


对我来说,这种方法特别有吸引力,因为我编写了一个小库,在编译时不知道所有可能的子类型。

不幸的是,就所提供的JSON而言,这是一个糟糕的设计,您所描述的就是您必须做的。如果您可以在Java中创建一个类层次结构来对数据进行建模,下面提供的答案会有所帮助,但如果这是适用的话,这将是您所能得到的最好结果。小提示:除了在
MyTypeModelDeserializer
中手动设置类型模型的属性外,您还可以调用
context.deserialize(json,TypeNModel.class)
对实际模型使用Gson的默认反序列化。注意:不要将
MyBaseTypeModel.class
作为类型传递,因为这将导致无限的反序列化循环。如果为子类注册专用类型适配器,则它们也将被
上下文调用。反序列化@MartinMatysiak说,要想在MyBaseTypeModel中使用GSON,你可以声明“类MyBaseTypeModelConcrete扩展了MyBaseTypeModel{}”然后传递MyBaseTypeModelConcrete.class。在这种情况下不有用,但在某些情况下可能有用。@DevrimTuncer我对Gson有类似的问题。想看看你是否能帮我。RuntimeTypeAdapterFactory是最终版本。如何扩展?我在我的项目中使其非最终版本:-)它在Gson的附加版本中,而不是在库中,您可以在中复制它。根据此文件中的文档,工厂不应直接实例化,而应通过
RuntimeTypeAdapterFactory.of
方法进行实例化。无论何时,答案通常是正确的,因此被低估了。谢谢。哇,比使用JsonDeserializer干净多了。谢谢。我尝试过在json中使用嵌套对象和列表的解决方案,它也可以(只要有一个区分对象的键,比如问题中的
“type1”
“type2”
)。
String jsonString = ...
BaseClass baseInstance = gson.fromJson(jsonString, BaseClass.class);