【问题标题】:Include enum type to serialize and deserialize with jackson包含枚举类型以使用杰克逊进行序列化和反序列化
【发布时间】:2017-09-10 08:40:22
【问题描述】:

我有一个简单的枚举,我想对其进行序列化和反序列化。该类如下所示:

    public enum TipusViatge {
    OCI,
    NEGOCIS,
    FAMILIA;

    @Override
    public String toString() {
        return name().toUpperCase();
    }
}

问题是,我通过 restful 调用发送它,接收方可能会接收任何类型(所以它只知道它将接收 Object)。所以杰克逊应该能够找出反序列化参数的类型。

有可能吗?我在想,在生成的 json 中包含类名应该允许杰克逊找出类型,但我一直无法这样做。

【问题讨论】:

    标签: java rest enums jackson


    【解决方案1】:

    我已经解决这个问题一段时间了。

    1st您可以使用Map<String, Object> 反序列化您的json。它总是有效的;你得到标准类型(你的枚举将被读取为纯字符串)。

    2nd 一般情况下,您始终知道您阅读的对象类型。这是顶级对象,您可以将其设置为 Jackson 映射器:mapper.readerFor(cls).readValue(json)。如果您的枚举是这个 cls 对象的一部分,那么 Jackson 知道类型并且只需读取值并对其进行解析。

    3rd 实际上,一个 json 字符串可以有多个对象。我说的是继承。您可以查看 Jackson 文档中的 @JsonTypeInfo

    4th 想象一下,您阅读了一个 json 源代码,但不知道您阅读的是什么。在这种情况下,您可以要求杰克逊在对象的开头写标记。就像您询问班级名称一样。我认为它与@JsonRootName 有关。你可以看这里:Jackson JSON Deserialization with Root Element

    我认为现在如何在 Jackson 中处理对象已经很清楚了。我的意思是我们知道如何告诉杰克逊我们想要反序列化什么元素。现在我们有一个问题:如何序列化 json -> 我们的枚举。

    5th这不是问题,而且开箱即用。 Jackson 使用name() 方法对枚举进行序列化,使用valueOf() 进行反序列化。您可以在杰克逊的EnumDeserializer 中仔细查看。

    6th 我不喜欢这种行为,因为它区分大小写。我遇到了当人们手动编写json字符串时,使用小写并且无法反序列化它的情况。此外,我相信,将枚举常量直接写入 json 文件是一种不好的做法,因为如果我想重构枚举的名称,所有存在的 json 字符串也应该修改(brrr)。为了解决这些问题,我做了以下技巧: 1. 使用parseId(String id) 的默认实现实现EnumId 接口,使用getId() 识别枚举常量并使用忽略大小写进行比较。 1.我在枚举中添加id字段 2.添加getId() - 用于序列化 3.添加parseId(String id) - 用于反序列化 4. 使用我的客户序列化程序在 Jackson ObjectMapper 添加新模块(它

    should use `getId()` instead of `name()`).
    if (enumId != null) {
        generator.writeString(enumId.getId());
    }
    
    1. 并告诉杰克逊如何反序列化这个枚举。这是一个困难的情况,因为在不同的来源中,Jackson 使用不同的反序列化层次结构,并且只是将另一个模块添加到具有自定义反序列化的 ObjectMapper (就像在 4 中一样。) 不会适用于所有情况。为了解决这个问题,我发现我们可以在枚举中的 parseId(String id) 方法中添加@JsonCreator,Jackson 将在所有情况下使用它。

    我认为这就是这个主题的全部内容。我给你一个代码示例,让你更清楚(最好写一次,然后解释两次):

    public interface EnumId {
    
        String name();
    
        default String getId() {
            return name().toLowerCase();
        }
    
        static <T extends Enum<?> & EnumId> T parseId(Class<T> cls, String id) {
            T res = parseId(cls.getEnumConstants(), id, null);
    
            if (res != null) {
                return res;
            }
    
            throw new EnumConstantNotPresentException(cls, id);
        }
    
        static <T extends EnumId> T parseId(T[] values, String id, T def) {
            for (T value : values) {
                if (id != null ? id.equalsIgnoreCase(value.getId()) : value.getId() == null) {
                    return value;
                }
            }
    
            return def;
        }
    
        static <T extends EnumId> T get(T value, T def) {
            return value != null ? value : def;
        }
    }
    
    public enum TipusViatge implements EnumId {
        OCI,
        NEGOCIS,
        FAMILIA;
    
        @JsonCreator
        public static TipusViatge parseId(String id) {
            return EnumId.parseId(TipusViatge.class, id);
        }
    }
    

    【讨论】:

    • 感谢您的 cmets @oleg.cherednik!就我而言,我不知道我将收到的类型。使用 Map 并反序列化为字符串是可行的,但由于我不知道枚举的类型,我需要以某种方式标记它们,以便 Jackon 可以反序列化它们。我正在尝试您的#JsonRootName 建议,我希望生成的 jsonized 字符串包含枚举类,但它没有。
    • 然后我考虑自定义序列化程序。对于您的枚举,您可以编写它并手动添加两个参数,其中一个。对于反序列化器,您可以读取类名,然后使用它来反序列化作为枚举常量的第二个参数。嗯。像那样。在我的脑海中,我看到了使用一些 ObjectMapper 设置的默认实现,但我现在不记得了。如果找到,我会查看并写信给她。
    • 这里是:new ObjectMapper().configure(SerializationFeature.WRAP_ROOT_VALUE, true).writeValueAsString(TipusViatge.FAMILIA)
    猜你喜欢
    • 1970-01-01
    • 2012-09-10
    • 1970-01-01
    • 1970-01-01
    • 2021-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多