【问题标题】:Genson Polymorphic / Generic SerializationGenson 多态/泛型序列化
【发布时间】:2015-11-06 13:32:29
【问题描述】:

我正在尝试在 Java 中使用 Genson 1.3 为多态类型实现 JSON 序列化,包括:

  • 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 应该如何解决这个序列化?

【问题讨论】:

    标签: java json serialization json-deserialization genson


    【解决方案1】:

    非常适合提供完整的示例!

    第一个问题是 DTO 没有无参数构造函数,但 Genson 支持类,即使具有带参数的构造函数。您只需通过带有“useConstructorWithArguments(true)”的构建器启用它。

    但是,这并不能解决完整的问题。目前,Genson 仅对序列化为 json 对象的类型提供完整的多态支持。因为 Genson 会为其添加一个名为 '@class' 的属性。有一个open issue

    可能适用于大多数情况的最佳解决方案是定义一个自动将所有值包装在 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 的一部分。

    【讨论】:

    • 我实现了一个基于此的解决方案以及FactoryChainedFactory 的javadocs,其中还提供了相关的代码示例。
    猜你喜欢
    • 2021-10-27
    • 2022-11-21
    • 1970-01-01
    • 2018-03-25
    • 1970-01-01
    • 2011-07-29
    • 1970-01-01
    • 2018-08-29
    • 2014-02-13
    相关资源
    最近更新 更多