java - Genson 多态/通用序列化

标签 java json serialization json-deserialization genson

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

  • 数字
  • 数组
  • 枚举

下面的 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 表单必须是一个 JSON 对象,其中包括:

  • 类型成员
  • 尊贵成员(member)

查看EnumConverter类,我发现我需要提供一个自定义Converter。但是我不太清楚如何正确注册 Converter 以便在反序列化期间调用它。如何使用 Genson 解决这个序列化问题?

最佳答案

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

第一个问题是 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,这将允许您委托(delegate)给默认转换器(这样它就可以自动与任何其他类型一起工作)。

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 的一部分就太好了。

关于java - Genson 多态/通用序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33568019/

相关文章:

java - 调用 IBM Mobilefirst 7.0-SAP JCo 适配器时出错

java - 从文件读取导致serialVersionUID错误

c# - 序列化/反序列化 "NHibernate Session",延迟初始化错误 ("StateServer mode"集群)

java - 通过 spring 初始化一个集合

java - Servlet 不会重定向我

javascript - 如何制作JSON骨架并稍后填充

javascript - $资源: expected response to contain an object but got an array

Python 迭代,FOR 循环将列表传递给 MySQL 查询中的参数

C# 对象到 XML

java - 如何比较 Jtextfield 中的 2 个整数