Це чудове питання, оскільки воно виділяє щось, що повинно бути простим, але насправді вимагає багато коду.
Для початку напишіть реферат, 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
), а потім викликає метод хука, beforeWrite()
щоб дозволити підкласу налаштувати це дерево. Аналогічно для десеріалізації з afterRead()
.
Далі ми підкласуємо це для конкретного MyClass
прикладу. Для ілюстрації я додаю на карту синтетичну властивість, що називається 'size', коли вона серіалізована. А для симетрії я її видалю, коли вона десеріалізована. На практиці це може бути будь-яка настройка.
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();
Нові типи Gson TypeAdapter та TypeAdapterFactory надзвичайно потужні, але вони також абстрактні та вимагають практики для ефективного використання. Сподіваємось, ви знайдете цей приклад корисним!