Gson - десериализация на конкретный тип объекта, основанный на значении поля

Я хочу десериализовать json-объекты для определенных типов объектов (используя библиотеку Gson) на основе значения поля type, например.:

[
    {
          "type": "type1",
          "id": "131481204101",
          "url": "http://something.com",
          "name": "BLAH BLAH",
          "icon": "SOME_STRING",
          "price": "FREE",
          "backgroundUrl": "SOME_STRING"
    },
    {
        ....
    }
]

Итак, поле type будет иметь разные (но известные) значения. Основываясь на этом значении, мне нужно десериализовать этот объект json для соответствующего объекта модели, например: Type1Model, Type2Model и т.д. Я знаю, что я могу легко сделать это до десериализации, переведя его на JSONArray, перейдя через него и разрешив, к какому типу он должен быть десериализован. Но я думаю, что это уродливый подход, и я ищу лучший способ. Любые предложения?

Ответ 1

Вы можете реализовать JsonDeserializer и использовать его при разборе значения Json для экземпляра Java. Я попытаюсь показать его с кодом, который даст вам идею:

1) Определите свой собственный класс JsonDeserializer, который создает другой экземпляр классов с помощью входящего свойства json value 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;
    }
}

2) Определите базовый класс для вашего другого экземпляра Java-объектов:

class  MyBaseTypeModel {
    private String type;
    // TODO : add other shared fields here
}

3) Определите ваш другой экземпляр классов объектов Java, которые расширяют ваш базовый класс:

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

class Type2Model extends MyBaseTypeModel {
    // TODO: add specific fields for this 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);

Я не могу проверить это прямо сейчас, но надеюсь, что вы поняли эту идею. Также эта ссылка будет очень полезна.

Ответ 2

использовать https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java

затем настройте его с помощью

public static final class JsonAdapterFactory extends 
    RuntimeTypeAdapterFactory<MediumSummaryInfo> {
        public JsonAdapterFactory() {
            super(MyBaseType.class, "type");
            registerSubtype(MySubtype1.class, "type1");
            registerSubtype(MySubtype2.class, "type2");
        }
}

и добавьте аннотацию:

@JsonAdapter(MyBaseType.JsonAdapterFactory.class)

в MyBaseType

Гораздо лучше.