繁体   English   中英

如何在注册 DefaultScalaModule 的 Spring 应用程序中使用 Jackson 将 JSON 对象反序列化为 Java 集合?

[英]How to deserialize a JSON object to a Java collection using Jackson in a Spring application that registered the DefaultScalaModule?

我正在编写一个通过 Spring 控制器提供 REST 端点的 Java 库。 一个端点的负载是我的JavaRoutine类的一个实例,我为它提供了一个 JSON 序列化器/反序列化器对。 这是(稍微简化):

@JsonSerialize(using = JavaRoutine.Serializer.class)
@JsonDeserialize(using = JavaRoutine.Deserializer.class)
public class JavaRoutine {
    private final String jobId;
    private final List<Object> inputValues;
    private final List<ExpressionType> inputTypes; // ExpressionType is defined in my lib

    public JavaRoutine(String jobId) {
        this.jobId = jobId;
        this.inputValues = new ArrayList<>();
        this.inputTypes = new ArrayList<>();
    }

    public String getJobId() { return jobId; }

    public void addInput(Object value) {
        inputValues.add(value);
        inputTypes.add(value == null ? null : ExpressionType.getTypeForValue(value));
    }

    public static class Serializer extends StdSerializer<JavaRoutine> {
        private static final ObjectMapper mapper = new ObjectMapper();

        public Serializer() {
            super(JavaRoutine.class);
        }

        @Override
        public void serialize(JavaRoutine routine, JsonGenerator gen, SerializerProvider provider) throws IOException {
            gen.writeStartObject();
            gen.writeStringField("jobId", routine.jobId);
            gen.writeArrayFieldStart("inputs");
            int inputCount = routine.inputValues.size();
            for (int i = 0; i < inputCount; i++) {
                gen.writeStartObject();
                gen.writeStringField("type", mapper.writeValueAsString(routine.inputTypes.get(i)));
                gen.writeStringField("value", mapper.writeValueAsString(routine.inputValues.get(i)));
                gen.writeEndObject();
            }
            gen.writeEndArray();
            gen.writeEndObject();
        }
    }

    public static class Deserializer extends StdDeserializer<JavaRoutine> {
        private static final ObjectMapper mapper = new ObjectMapper();

        public Deserializer() {
            super(JavaRoutine.class);
        }

        @Override
        public JavaRoutine deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            Map<String, Object> fields = p.readValueAs(new TypeReference<Map<String, Object>>() {});
            JavaRoutine routine = new JavaRoutine((String) fields.get("jobId");
            List<Map<String, String>> inputs = (List<Map<String, String>>) fields.get("inputs");
            for (Map<String, String> input: inputs) {
                ExpressionType inputType = mapper.readValue(input.get("type"), ExpressionType.class);
                Object inputValue = inputType == null ? null : mapper.readValue(input.get("value"), inputType.getJavaType());
                routine.addInput(inputValue);
            }
            return routine;
        }
    }
}

这有效。 除非链接库的应用程序已经为 Scala注册了Jackson 模块,这是它自己需要的。 (简而言之,这个 Jackson 模块的目的是将 JSON 结构反序列化为 Scala 集合,而不是 Java 集合。)因此,对p.readValueAs()的调用将“输入”数组反序列化为 Scala 列表,其中导致在两行后转换为List<Map<String, String>>失败。

你会推荐什么解决方案?

没试过你的例子。 但是在谷歌的多个节点上运行 Kubernetes 并且在节点之间跳转时得到了奇怪的 scala 集合对象。 这帮助了我一半。

尝试在(我的猜测)解串器中创建如下映射器。

ObjectMapper mapper.registerModule(new DefaultScalaModule());

Scala 映射也有一些问题。 对我来说,现在订单没有保留。 所以 Lists 和 Maps (LinkedHashMap) 会失去原来的顺序。 :(

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM