Простое использование TypeAdapterFactory

AFAIK самая гибкая настройка gson возможна через TypeAdapterFactory, однако она может оказаться излишне сложной. Это заставляет меня писать для каждого обработанного класса как read, так и write, хотя иногда нужен только один метод. Более того, иногда JsonSerializer и/или JsonDeserializer было гораздо проще писать, например. как здесь. Это приводит меня к этим вопросам:

  • Можно ли написать TypeAdapter, который просто делегирует один из своих методов (например, запись ImmutableList в запись List)?
  • Можно ли как-то использовать JsonSerializer и/или JsonDeserializer вместе с TypeAdapterFactory? В качестве альтернативы, существует ли для них factory?

Ответы

Ответ 1

Можно создать TypeAdapter, который делегирует один из своих методов. Этот вариант использования является важной частью API, и для этой цели существует метод getDelegateAdapter(). Передайте this в качестве первого аргумента getDelegateAdapter, который вернет адаптер, который имеет приоритет после текущего factory.

TypeAdapterFactory immutableListFactory = new TypeAdapterFactory() {
  @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
    if (!(type.getType() instanceof ParameterizedType)
        || !type.getRawType().equals(ImmutableList.class)) {
      return null;
    }

    ParameterizedType parameterizedType = (ParameterizedType) type.getType();
    TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
    TypeAdapter<?> elementAdapter = gson.getAdapter(
        TypeToken.get(parameterizedType.getActualTypeArguments()[0]));
    return new ImmutableListAdapter(delegate, elementAdapter);
  }

  class ImmutableListAdapter<E> extends TypeAdapter<ImmutableList<E>> {
    private TypeAdapter<List<E>> delegate;
    private TypeAdapter<E> element;

    ImmutableListAdapter(TypeAdapter<List<E>> delegate, TypeAdapter<E> element) {
      this.delegate = delegate;
      this.element = element;
    }

    @Override public void write(JsonWriter out, ImmutableList<E> value) throws IOException {
      delegate.write(out, value);
    }

    @Override public ImmutableList<E> read(JsonReader in) throws IOException {
      if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
      }
      ImmutableList.Builder<E> builder = ImmutableList.builder();
      in.beginArray();
      while (in.hasNext()) {
        builder.add(element.read(in));
      }
      in.endArray();
      return builder.build();
    }
  }
};

Вы можете смешивать и сопоставлять JsonSerializer/JsonDeserializer с TypeAdapterFactory, но не напрямую. Самый простой способ - перезвонить в Gson для сериализации дочерних значений в вашем классе. В этом примере мы изменили бы внутренний цикл на это:

      while (in.hasNext()) {
        builder.add(gson.<E>fromJson(in, elementType));
      }

Основное различие между JsonSerializer/JsonDeserializer и TypeAdapter заключается в том, сколько этапов требуется для перехода от JSON к вашей объектной модели. При JsonSerializer/JsonDeserializer объекты сначала преобразуются в модель Gson DOM (JsonElement и т.д.), А затем преобразуются в вашу объектную модель. С TypeAdapter промежуточный шаг пропускается.

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