Пользовательский сериализатор Gson для одной переменной (из многих) в объекте с помощью TypeAdapter



Я видел много простых примеров использования пользовательского TypeAdapter. Самым полезным было Class TypeAdapter<T>. Но это еще не ответ на мой вопрос.



Я хочу настроить сериализацию одного поля в объекте и позволить механизму Gson по умолчанию позаботиться об остальном.



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



public class MyClass extends SomeClass {

@Expose private HashMap<String, MyObject1> lists;
@Expose private HashMap<String, MyObject2> sources;
private LinkedHashMap<String, SomeClass> customSerializeThis;
[snip]
}
633   3  

3 ответов:

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

для начала напишите реферат TypeAdapterFactory что дает вам крючки для изменения исходящих данных. В этом примере используется новый API в Gson 2.2 под названием getDelegateAdapter() это позволяет вам искать адаптер, который Gson будет использовать по умолчанию. Адаптеры делегатов чрезвычайно удобны, если вы просто хотите настроить стандартное поведение. И в отличие от полных адаптеров пользовательского типа, они будут будьте в курсе автоматически, как вы добавляете и удаляете поля.

public abstract class CustomizedTypeAdapterFactory<C>
    implements TypeAdapterFactory {
  private final Class<C> customizedClass;

  public CustomizedTypeAdapterFactory(Class<C> customizedClass) {
    this.customizedClass = customizedClass;
  }

  @SuppressWarnings("unchecked") // we use a runtime check to guarantee that 'C' and 'T' are equal
  public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
    return type.getRawType() == customizedClass
        ? (TypeAdapter<T>) customizeMyClassAdapter(gson, (TypeToken<C>) type)
        : null;
  }

  private TypeAdapter<C> customizeMyClassAdapter(Gson gson, TypeToken<C> type) {
    final TypeAdapter<C> delegate = gson.getDelegateAdapter(this, type);
    final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
    return new TypeAdapter<C>() {
      @Override public void write(JsonWriter out, C value) throws IOException {
        JsonElement tree = delegate.toJsonTree(value);
        beforeWrite(value, tree);
        elementAdapter.write(out, tree);
      }
      @Override public C read(JsonReader in) throws IOException {
        JsonElement tree = elementAdapter.read(in);
        afterRead(tree);
        return delegate.fromJsonTree(tree);
      }
    };
  }

  /**
   * Override this to muck with {@code toSerialize} before it is written to
   * the outgoing JSON stream.
   */
  protected void beforeWrite(C source, JsonElement toSerialize) {
  }

  /**
   * Override this to muck with {@code deserialized} before it parsed into
   * the application type.
   */
  protected void afterRead(JsonElement deserialized) {
  }
}

приведенный выше класс использует сериализацию по умолчанию для получения дерева JSON (представленного JsonElement), а затем вызывает метод hook beforeWrite() чтобы позволить подклассу настроить это дерево. Аналогично для десериализации с afterRead().

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

private class MyClassTypeAdapterFactory extends CustomizedTypeAdapterFactory<MyClass> {
  private MyClassTypeAdapterFactory() {
    super(MyClass.class);
  }

  @Override protected void beforeWrite(MyClass source, JsonElement toSerialize) {
    JsonObject custom = toSerialize.getAsJsonObject().get("custom").getAsJsonObject();
    custom.add("size", new JsonPrimitive(custom.entrySet().size()));
  }

  @Override protected void afterRead(JsonElement deserialized) {
    JsonObject custom = deserialized.getAsJsonObject().get("custom").getAsJsonObject();
    custom.remove("size");
  }
}

наконец собрать все это вместе, создав настроенный Gson экземпляр, который использует адаптер нового типа:

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(new MyClassTypeAdapterFactory())
    .create();
Дсын это TypeAdapter и TypeAdapterFactory типы чрезвычайно мощные, но они также абстрактны и требуют практики для эффективного использования. Надеюсь, вы найдете этот пример полезен!

есть другой подход к этому. Как говорит Джесси Уилсон, это должно быть легко. И знаете что, это и легко!

если вы реализуете JsonSerializer и JsonDeserializer для вашего типа, вы можете обрабатывать детали, которые вы хотите и делегировать в Gson для всего остального, С очень мало кода. Я цитирую из @Perception ответ на другой вопрос ниже для удобства см. Этот ответ для получения более подробной информации:

в этом случае лучше использовать JsonSerializer в противоположность TypeAdapter, по той простой причине, что сериализаторы имеют доступ к их контексту сериализации.

public class PairSerializer implements JsonSerializer<Pair> {
    @Override
    public JsonElement serialize(final Pair value, final Type type,
            final JsonSerializationContext context) {
        final JsonObject jsonObj = new JsonObject();
        jsonObj.add("first", context.serialize(value.getFirst()));
        jsonObj.add("second", context.serialize(value.getSecond()));
        return jsonObj;
    }
}

главным преимуществом этого (помимо избежания сложных обходных путей) является то, что вы все еще можете использовать преимущества других адаптеров типа и пользовательских сериализаторов, которые могли быть зарегистрированы в основном контексте. Обратите внимание, что регистрация сериализаторов и адаптеров использует то же самое код.

тем не менее, я признаю, что подход Джесси выглядит лучше, если вы часто собираетесь изменять поля в своем объекте Java. Это компромисс простоты использования против гибкости, возьмите свой выбор.

мой коллега также упомянул использование @JsonAdapter аннотации

https://google.github.io/gson/apidocs/com/google/gson/annotations/JsonAdapter.html

пример:

 private static final class Gadget {
   @JsonAdapter(UserJsonAdapter2.class)
   final User user;
   Gadget(User user) {
       this.user = user;
   }
 }

Comments

    Ничего не найдено.