Jackson - Сериализация EnumValues ​​завершается неудачно при динамическом добавлении значений enum - PullRequest
1 голос
/ 31 октября 2019

Я использую Spring Boot, и у меня возникает ошибка при попытке преобразовать Bean в JSON с Jackson.

Когда эта функция (которая находится внутри @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;
    ...
}

Есть ли у Джексона кеш перечисления и, следовательно, он не может найти новое значение? Если это так, могу ли я обновить его?

РЕДАКТИРОВАТЬ:

Я нашел источник проблемы.

Внутри Джексона 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.

Есть ли способ включить SerializationFeature.WRITE_ENUMS_USING_TO_STRING только длянекоторые EnumSerializer? Потому что это может иметь побочные эффекты, если я включу его глобально.

1 Ответ

1 голос
/ 02 ноября 2019

Поскольку метод 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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...