Gson десериализует массив JSON с несколькими типами объектов

У меня есть странный JSON, как:

[
  {
    "type":"0",
    "value":"my string"
  },
  {
    "type":"1",
    "value":42
  },
  {
    "type":"2",
    "value": {
    }
  }
]

Основанный на некотором поле, объект в массиве имеет определенный тип. Используя Gson, я подумал о том, чтобы иметь TypeAdapterFactory, который отправляет адаптеры делегатов для этих определенных типов в TypeAdapter, но я зациклен на понимании хорошего способа чтения этого поля типа, чтобы узнать, какой тип создать. В адаптере типов

Object read(JsonReader in) throws IOException {
  String type = in.nextString();
  switch (type) {
    // delegate to creating certain types.
  }
}

Предполагается, что поле "тип" идет первым в моем JSON. Есть ли достойный способ снять это предположение?

1 ответ

Решение

Вот некоторый код, который я написал для обработки массива элементов NewsFeedArticle и NewsFeedAd в Json. Оба элемента реализуют интерфейс маркера NewsFeedItem, чтобы я мог легко проверить, должен ли TypeAdater использоваться для определенного поля.

public class NewsFeedItemTypeAdapterFactory implements TypeAdapterFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        if (!NewsFeedItem.class.isAssignableFrom(type.getRawType())) {
            return null;
        }

        TypeAdapter<JsonElement> jsonElementAdapter = gson.getAdapter(JsonElement.class);
        TypeAdapter<NewsFeedArticle> newsFeedArticleAdapter = gson.getDelegateAdapter(this, TypeToken.get(NewsFeedArticle.class));
        TypeAdapter<NewsFeedAd> newsFeedAdAdapter = gson.getDelegateAdapter(this, TypeToken.get(NewsFeedAd.class));

        return (TypeAdapter<T>) new NewsFeedItemTypeAdapter(jsonElementAdapter, newsFeedArticleAdapter, newsFeedAdAdapter).nullSafe();
    }

    private static class NewsFeedItemTypeAdapter extends TypeAdapter<NewsFeedItem> {

        private final TypeAdapter<JsonElement> jsonElementAdapter;
        private final TypeAdapter<NewsFeedArticle> newsFeedArticleAdapter;
        private final TypeAdapter<NewsFeedAd> newsFeedAdAdapter;

        NewsFeedItemTypeAdapter(TypeAdapter<JsonElement> jsonElementAdapter,
                TypeAdapter<NewsFeedArticle> newsFeedArticleAdapter,
                TypeAdapter<NewsFeedAd> newsFeedAdAdapter) {
            this.jsonElementAdapter = jsonElementAdapter;
            this.newsFeedArticleAdapter = newsFeedArticleAdapter;
            this.newsFeedAdAdapter = newsFeedAdAdapter;
        }

        @Override
        public void write(JsonWriter out, NewsFeedItem value) throws IOException {
            if (value.getClass().isAssignableFrom(NewsFeedArticle.class)) {
                newsFeedArticleAdapter.write(out, (NewsFeedArticle) value);

            } else if (value.getClass().isAssignableFrom(NewsFeedAd.class)) {
                newsFeedAdAdapter.write(out, (NewsFeedAd) value);

            }

        }

        @Override
        public NewsFeedItem read(JsonReader in) throws IOException {
            JsonObject objectJson = jsonElementAdapter.read(in).getAsJsonObject();

            if (objectJson.has("Title")) {
                return newsFeedArticleAdapter.fromJsonTree(objectJson);

            } else if (objectJson.has("CampaignName")) {
                return newsFeedAdAdapter.fromJsonTree(objectJson);
            }

            return null;
        }
    }
}

Затем вы можете зарегистрировать это в Gson, используя следующий код.

return new GsonBuilder()
        .registerTypeAdapterFactory(new NewsFeedItemTypeAdapterFactory())
        .create();
Другие вопросы по тегам