【问题标题】:Jackson deserialize different strings to the same enum constantJackson 将不同的字符串反序列化为相同的枚举常量
【发布时间】:2018-06-05 16:17:42
【问题描述】:

假设我有一个看起来像这样的enum Status

public enum Status {
    SUCCESS,
    FAIL,
    RETRY,
    UNKNOWN
}

我收到来自 JSON 的 status 属性,可能类似于以下示例。

  • {"status":"success"} // 有效大小写,反序列化为Status.SUCCESS
  • {"status":"fail"} // 有效大小写,反序列化为Status.FAIL
  • {"status":"retry"} // 有效大小写,反序列化为Status.RETRY

但任何其他值都应反序列化为Status.UNKNOWN。例子。

  • {"status":"blabla"} // 大小写无效,反序列化为Status.UNKNOWN
  • {"status":"John"} // 无效大小写,反序列化为Status.UNKNOWN

我知道我可以通过编写自定义反序列化器来做到这一点,但我会尽量避免这种情况,因为我的程序中有很多很多枚举,并且为每个枚举都需要一个自定义反序列化器会有点过头了。

理想情况下,正则表达式中的某种构造函数可以匹配任何字符串(“成功”、“失败”和“重试”除外)。

有没有办法在不编写自定义反序列化器的情况下使用 Jackson?

【问题讨论】:

    标签: java serialization enums jackson


    【解决方案1】:

    如果您的所有枚举都具有UNKNOWN 值,您可以编写一个自定义反序列化器,如下所示:

    class EnumDeserializer extends JsonDeserializer<Enum> {
    
        private final Class<? extends Enum> enumType;
    
        public EnumDeserializer(Class<? extends Enum> enumType) {
            this.enumType = enumType;
        }
    
        @Override
        public Enum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            try {
                String stringValue = jp.getValueAsString().toUpperCase();
                return Enum.valueOf(enumType, stringValue.toUpperCase());
            } catch (IllegalArgumentException e) {
                return Enum.valueOf(enumType, "UNKNOWN");
            }
        }
    }
    

    并配置您的映射器以使用它:

    SimpleModule module = new SimpleModule();
    module.setDeserializerModifier(new BeanDeserializerModifier() {
        @Override
        public JsonDeserializer<Enum> modifyEnumDeserializer(DeserializationConfig config,
                                                             final JavaType type,
                                                             BeanDescription beanDesc,
                                                             final JsonDeserializer<?> deserializer) {
    
            return new EnumDeserializer((Class<Enum<?>>) type.getRawClass());
        }
    });
    
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(module);
    

    或者,您可以使用 jackson 反序列化功能为未知枚举属性设置默认值:

    enum MyEnum { A, B, @JsonEnumDefaultValue UNKNOWN }
    ...
    final ObjectMapper mapper = new ObjectMapper();
    mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
    
    MyEnum value = mapper.readValue("\"foo\"", MyEnum.class);
    assertSame(MyEnum.UNKNOWN, value);
    

    但是使用这种方法,您需要更改所有枚举以使用 @JsonEnumDefaultValue 注释作为默认值,而且默认情况下它不处理小写枚举值。

    【讨论】:

    • 请问您是如何解决这个问题的?我花了两天时间试图追踪jackson-databind的源代码,但一无所获......
    • 有什么“更简单”的解决方案吗?也许只使用注释?
    猜你喜欢
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-27
    • 1970-01-01
    • 2013-09-04
    • 1970-01-01
    • 2016-10-16
    相关资源
    最近更新 更多