Jackson - 动态添加枚举值时,EnumValues 序列化失败

Posted

技术标签:

【中文标题】Jackson - 动态添加枚举值时,EnumValues 序列化失败【英文标题】:Jackson - EnumValues serialization fails when dynamically adding enum values 【发布时间】:2020-02-27 00:11:37 【问题描述】:

我正在使用Spring Boot,当它尝试使用JacksonBean 转换为JSON 时出现错误。

当这个函数(在 @RestController 中)第一次被调用时,它工作得很好:

@GetMapping
public UserDto getUser() 
    UserDto userDto = // get the user 
    // userDto.getRoles() is an enum, let's call it XEnum, with 20 entries
    return userDto;

在此之后,一个新值被动态添加到XEnum,所以这个枚举现在有 21 个条目。

当上面的函数被调用时,我得到这个错误:

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: 41; nested exception is com.fasterxml.jackson.databind.JsonMappingException: 41 (through reference chain: ...UserDto["roles"]->java.util.ArrayList[21])
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:296)
    at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:103)
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:290)
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:180)
    ...
Caused by: java.lang.ArrayIndexOutOfBoundsException: 41
    at com.fasterxml.jackson.databind.util.EnumValues.serializedValueFor(EnumValues.java:79)
    at com.fasterxml.jackson.databind.ser.std.EnumSerializer.serialize(EnumSerializer.java:132)
    at com.fasterxml.jackson.databind.ser.std.EnumSerializer.serialize(EnumSerializer.java:27)
    at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContentsUsing(IndexedListSerializer.java:142)

在我的实际情况中,41 是枚举中新条目的序数值。

UserDto 类如下所示:

public class UserDto implements Serializable 

    private static final long serialVersionUID = 475464704956181923L;

    private XEnum currentRole;
    private List<XEnum> roles;
    ...

Jackson 是否有枚举缓存,因此找不到新值?如果是这样,我可以刷新它吗?

编辑:

我找到了问题的根源。

在序列化过程中使用的 Jackson EnumSerializer 内部,serialize 方法如下所示:

@Override
public final void serialize(Enum<?> en, JsonGenerator gen, SerializerProvider serializers)
    throws IOException

    // [JACKSON-684]: serialize as index?
    if (_serializeAsIndex(serializers)) 
        gen.writeNumber(en.ordinal());
        return;
    
    // [databind#749]: or via toString()?
    if (serializers.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) 
        gen.writeString(en.toString());
        return;
    
    gen.writeString(_values.serializedValueFor(en)); // this line is evaluated !!

_values 包含预先解析的值,不包含新的 Enum 条目。

有没有办法只为某些 EnumSerializer 启用 SerializationFeature.WRITE_ENUMS_USING_TO_STRING ?因为如果我全局打开它可能会产生副作用。

【问题讨论】:

请加UserDto型号 我已经添加了相关数据。 酷,谢谢private List&lt;@Enumerated(EnumType.STRING) XEnum&gt; roles 应该可以解决你的问题。 当我尝试你的语句时编译失败:annotation type not applicable to this kind of declaration. 好的,所以添加这个@ElementCollection(targetClass = XEnum.class) @Enumerated(EnumType.STRING) private XEnum currentRole; 【参考方案1】:

由于EnumSerializer#serialize 方法和com.fasterxml.jackson.databind.util.EnumValues 类被标记为final,因此很难扩展它们的行为。在这种情况下,最简单的解决方案是为 XEnum 编写自定义序列化程序

class DynamicEnumJsonSerializer extends JsonSerializer<Enum> 

    @Override
    public void serialize(Enum value, JsonGenerator gen, SerializerProvider serializers) throws IOException 
        gen.writeString(value.toString());
    

您可以像下面这样注册它:

@JsonSerialize(using = DynamicEnumJsonSerializer.class)
enum XEnum 
    X, Y, Z

【讨论】:

这是完美的。谢谢!

以上是关于Jackson - 动态添加枚举值时,EnumValues 序列化失败的主要内容,如果未能解决你的问题,请参考以下文章

Jackson全面解析--注解全讲解十二(动态添加字段@JsonAppend)

枚举 $EnumName 不能表示非枚举值:“$EnumValue”。您的意思是枚举值“$EnumValue”吗

Jackson:将枚举值序列化和反序列化为整数

枚举的反序列化不起作用 - Jackson [重复]

反序列化 null 值以使用 Jackson 枚举

Jackson 使用枚举键、POJO 值反序列化为 Map