Добавление объекта с использованием пользовательского типа адаптера, 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();
        }
    };
}

Кто-нибудь знает лучший способ сделать это?