Добавление объекта с помощью пользовательского адаптера типов, jsonwriter в gson
gson - отличная библиотека - она хорошо работает. Иногда у меня есть пользовательские требования, и я могу создавать и регистрировать TypeAdapters и TypeAdaptorFactories - и это тоже хорошо работает.
Что меня смущает, так это то, как делегировать обратно в сериализацию json... Большую часть времени мне это нужно для коллекций, но чтобы проиллюстрировать суть - предположим, у меня был парный класс, который gson, очевидно, успешно сериализовал, но по какой-то причине Мне был нужен мой собственный сериализатор. Хорошо... если моя пара
public class Pair
{
public final Object First;
public final Object Second;
public Pair( Object first, Object second) {this.first = first; this.second = second};
}
Если бы я написал адаптер типа для этого - вы бы хотели, чтобы функция write была похожа на:
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, которая принимает значение и записывающее устройство - но также принимает и знак типа - которого у меня нет. У меня складывается впечатление, что я должен иметь другой тип адаптера откуда-то - но когда у меня просто есть список объектов... где я могу это получить? я могу получить адаптер для объекта?
Я немного запутался? Конечно, это самый распространенный вариант использования? Большинство пользовательских сериализаторов будут для странного списка T или дерева T или чего-то еще, и вы действительно не знаете, что находится в списке, кроме того, что он наследуется от T... так что вы должны иметь возможность делегировать обратно сериализация каким-то образом?
В любом случае - если кто-то может сказать мне, как написать вышеупомянутую функцию, я был бы очень благодарен!
2 ответа
В этом случае лучше использовать 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();
}
}
На самом деле это, похоже, сработало - получение адаптера для объекта - с помощью 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();
}
};
}
кто-нибудь знает лучший способ сделать это?