Добавление объекта с использованием пользовательского типа адаптера, jsonwriter в gson
gson - отличная библиотека - она работает хорошо. Иногда у меня есть пользовательские требования, а также можно создавать и регистрировать TypeAdapters и TypeAdaptorFactories - и это тоже хорошо работает.
Что меня смущает, так это то, как делегировать обратно в сериализацию json... В большинстве случаев мне это нужно для коллекций, но для иллюстрации точки - предположим, что у меня был парный класс, который, несомненно, сериализовал бы сериал, но по какой-то причине мне нужен мой собственный сериализатор. Ну... если моя пара
public class Pair
{
public final Object First;
public final Object Second;
public Pair( Object first, Object second) {this.first = first; this.second = second};
}
Если я написал адаптер типа для этого - вы хотели бы, чтобы функция записи выглядела так:
public void write( JsonWriter out, Pair pair )
{
out.beginObject();
out.name( "first");
out.value( pair.first ); // can't do this
out.name( "second");
out.value( pair.second); // or this
out.endObject();
}
Итак, вы можете увидеть проблему - я понятия не имею о типах первого и второго, а также о том, как они сериализуются. Я могу использовать gson.toJson для сериализации первого и второго, но если я добавлю их в качестве строки для писателя, они будут экранированы. Существует функция gson.tojson, которая берет значение и писателя, но она также принимает typetoken, чего у меня нет. У меня такое впечатление, что я имею в виду, что у меня есть другой адаптер типа, но когда у меня есть список объектов... где я могу это получить? я просто получаю адаптер для объекта?
Я немного смущен? Наверняка это самый распространенный вариант использования? Большинство пользовательских сериализаторов будут для странного списка T или дерева T или чего-то еще, и вы действительно не знаете, что находится в списке, помимо того, что оно наследуется от T... поэтому вам нужно иметь возможность делегировать сериализация каким-то образом?
В любом случае - если кто-то может сказать мне, как написать вышеприведенную функцию, я бы очень признателен!
Ответы
Ответ 1
В этом случае лучше использовать JsonSerializer
в отличие от TypeAdapter
, по той простой причине, что сериализаторы имеют доступ к контексту сериализации:
public class PairSerializer implements JsonSerializer<Pair> {
public PairSerializer() {
super();
}
@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;
}
}
В приведенном выше примере кода показано, как делегировать сериализацию целевых объектов обратно на главный маршаллер. Основное преимущество этого (помимо избежания сложных обходных решений) заключается в том, что вы все еще можете использовать другие адаптеры типов и пользовательские сериализаторы, которые могли быть зарегистрированы в основном контексте. Обратите внимание, что для регистрации сериализаторов и адаптеров используется тот же код:
// With adapter
final Gson gson = new GsonBuilder().registerTypeAdapter(Pair.class,
new PairAdapter()).create();
// With serializer
final Gson gson = new GsonBuilder().registerTypeAdapter(Pair.class,
new PairSerializer()).create();
Если вы обнаружите, что вам нужно придерживаться адаптера, вы можете использовать встроенный прокси-сервер Gson для сериализации свойств Pair для вас, при этом вы потеряете доступ к пользовательским регистрациям, которые вы сделали на родительском прокси-сервере Gson:
public class PairAdapter extends TypeAdapter<Pair> {
final Gson embedded = new Gson();
public PairAdapter() {
super();
}
@Override
public void write(final JsonWriter out, final Pair value)
throws IOException {
out.beginObject();
out.name("first");
embedded.toJson(embedded.toJsonTree(value.getFirst()), out);
out.name("second");
embedded.toJson(embedded.toJsonTree(value.getSecond()), out);
out.endObject();
}
@Override
public Pair read(JsonReader in) throws IOException {
throw new UnsupportedOperationException();
}
}
Ответ 2
на самом деле это работало - получение адаптера для объекта - с помощью метода typeadaptorfactory для обнаружения метода "json" для объекта и хранения этого объекта адаптера. Кажется удивительно сложным для того, что является таким очевидным прецедентом?
например:
public static @Nullable
TypeAdapter<ImmutableStack<?>> json(final Gson gson)
{
final TypeAdapter<Object> adaptor = gson.getAdapter(Object.class);
return new TypeAdapter<ImmutableStack<?>>()
{
@Override
public ImmutableStack<?> read(@Nullable final JsonReader in) throws IOException
{
throw ProgramError.notImplementedYet();
}
@Override
public void write(final JsonWriter out, final ImmutableStack<?> value) throws IOException
{
out.beginArray();
for (final Object inner : value)
adaptor.write(out, inner);
out.endArray();
}
};
}
Кто-нибудь знает лучший способ сделать это?