Genson Полиморфная/общая сериализация

Я пытаюсь реализовать сериализацию JSON на Java с Genson 1.3 для полиморфных типов, в том числе:

  • Numbers
  • Массивы
  • Enum занятий

SSCCE ниже примерно демонстрирует, чего я пытаюсь достичь:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.owlike.genson.Genson;
import com.owlike.genson.GensonBuilder;

/**
 * A Short, Self Contained, Compilable, Example for polymorphic serialization
 * and deserialization.
 */
public class GensonPolymoprhicRoundTrip {

    // our example enum
    public static enum RainState {
        NO_RAIN,
        LIGHT_RAIN,
        MODERATE_RAIN,
        HEAVY_RAIN,
        LIGHT_SNOW,
        MODERATE_SNOW,
        HEAVY_SNOW;
    }

    public static class Measurement<T> {
        public T value;
        public int qualityValue;
        public String source;

        public Measurement() {
        }
        public Measurement(T value, int qualityValue, String source) {
            this.value = value;
            this.qualityValue = qualityValue;
            this.source = source;
        }
    }

    public static class DTO {
        public List<Measurement<?>> measurements;

        public DTO(List<Measurement<?>> measurements) {
            this.measurements = measurements;
        }
    }

    public static void main(String... args) {
        Genson genson = new GensonBuilder()
        .useIndentation(true)
        .useRuntimeType(true)
        .useClassMetadataWithStaticType(false)
        .addAlias("RainState", RainState.class)
        .useClassMetadata(true)
        .create();

        DTO dto = new DTO(
                new ArrayList(Arrays.asList(
                        new Measurement<Double>(15.5, 8500, "TEMP_SENSOR"),
                        new Measurement<double[]>(new double[] {
                                2.5,
                                1.5,
                                2.0
                        }, 8500, "WIND_SPEED"),
                        new Measurement<RainState>(RainState.LIGHT_RAIN, 8500, "RAIN_SENSOR")
                        )));
        String json = genson.serialize(dto);
        System.out.println(json);
        DTO deserialized = genson.deserialize(json, DTO.class);
    }
}

Числа и массивы хорошо работали «из коробки», но класс перечисления представляет некоторую проблему. В этом случае сериализованная форма JSON должна быть IMO объектом JSON, включая:

  • член типа
  • элемент значения

Глядя на класс EnumConverter, я вижу, что мне нужно будет предоставить собственный класс Converter. Однако я не совсем понимаю, как правильно зарегистрировать Converter, чтобы он вызывался во время десериализации. Как решить эту сериализацию с помощью Genson?


person Eero Aaltonen    schedule 06.11.2015    source источник


Ответы (1)


Отлично подходит для предоставления полного примера!

Первая проблема заключается в том, что DTO не имеет конструктора без аргументов, но Genson поддерживает классы даже с конструкторами, у которых есть аргументы. Вам просто нужно включить его через конструктор с помощью «useConstructorWithArguments (true)».

Однако это не решит всей проблемы. На данный момент Genson имеет полную полиморфную поддержку только для типов, сериализованных как объект json. Потому что Генсон добавит к нему свойство под названием «@class». Для этого есть открытая проблема.

Вероятно, лучшим решением, которое должно работать в большинстве ситуаций, было бы определение преобразователя, который автоматически упаковывает все значения в объекты json, чтобы преобразователь, обрабатывающий метаданные класса, мог их сгенерировать. Это может быть «достаточно хорошим» решением, если ждать его официальной поддержки со стороны Genson.

Итак, сначала определите конвертер обертки

public static class LiteralAsObjectConverter<T> implements Converter<T> {
    private final Converter<T> concreteConverter;

    public LiteralAsObjectConverter(Converter<T> concreteConverter) {
        this.concreteConverter = concreteConverter;
    }

    @Override
    public void serialize(T object, ObjectWriter writer, Context ctx) throws Exception {
        writer.beginObject().writeName("value");
        concreteConverter.serialize(object, writer, ctx);
        writer.endObject();
    }

    @Override
    public T deserialize(ObjectReader reader, Context ctx) throws Exception {
        reader.beginObject();
        T instance = null;
        while (reader.hasNext()) {
            reader.next();
            if (reader.name().equals("value")) instance = concreteConverter.deserialize(reader, ctx);
            else throw new IllegalStateException(String.format("Encountered unexpected property named '%s'", reader.name()));
        }
        reader.endObject();
        return instance;
    }
}

Затем вам нужно зарегистрировать его в ChainedFactory, что позволит вам делегировать преобразователь по умолчанию (таким образом, он работает автоматически с любым другим типом).

Genson genson = new GensonBuilder()
            .useIndentation(true)
            .useConstructorWithArguments(true)
            .useRuntimeType(true)
            .addAlias("RainState", RainState.class)
            .useClassMetadata(true)
            .withConverterFactory(new ChainedFactory() {
                @Override
                protected Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) {
                    if (Wrapper.toAnnotatedElement(nextConverter).isAnnotationPresent(HandleClassMetadata.class)) {
                      return new LiteralAsObjectConverter(nextConverter);
                    } else {
                      return nextConverter;
                    }
                }
            }).create();

Недостатком этого решения является то, что для useClassMetadataWithStaticType необходимо установить значение true... но я думаю, что это приемлемо, поскольку это оптимум и может быть исправлено, но подразумевает некоторые изменения в коде Gensons, остальное до сих пор работает.

Если вы чувствуете интерес к этой проблеме, было бы здорово, если бы вы попытались решить эту проблему и открыли PR, чтобы предоставить эту функцию как часть Genson.

person eugen    schedule 06.11.2015
comment
Я реализовал решение на основе этого и javadocs для Factory и ChainedFactory , в котором также приведены соответствующие примеры кода. - person Eero Aaltonen; 11.11.2015