ObjectMapper не может десериализоваться без конструктора по умолчанию после обновления до Spring Boot 2

У меня есть следующие DTO:

@Value
public class PracticeResults {
    @NotNull
    Map<Long, Boolean> wordAnswers;
}

@Value
public class ProfileMetaDto {

    @NotEmpty
    String name;
    @Email
    String email;
    @Size(min = 5)
    String password;
}

@Value представляет собой аннотацию Lombok, которая генерирует конструктор. Это означает, что этот класс не имеет конструктора no-arg.

Я использовал Spring Boot 1.4.3.RELEASE и ObjectMapper bean смог десериализовать такой объект из json.

После обновления до Spring Boot 2.0.0.M7 я получаю следующее исключение:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of PracticeResults (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

Версия Jackson, используемая в Spring Boot 1.4.3, составляет 2.8.10 а для Spring Boot 2.0.0.M7 - 2.9.2.

Я попытался Google решить эту проблему, но нашел только решения с @JsonCreator или @JsonProperty.

Итак, почему он работает с Spring Boot 1.4.3 и не работает с Spring Boot 2? Можно ли настроить bean для того, чтобы вести себя так же, как и более старая версия?

Ответ 1

Из-за критических изменений в Lombok версии 1.16.20 вам необходимо установить следующее свойство в вашем файле lombok.config (если у вас нет этого файла, вы можете создать его в корневом каталоге вашего проекта):

lombok.anyConstructor.addConstructorProperties=true

Это описано в журнале изменений Lombok: https://projectlombok.org/changelog.

После этого Джексон должен снова принять @Value.

Вы можете быть заинтересованы в том, чтобы следить за связанной проблемой GitHub здесь, хотя это о @Data: https://github.com/rzwitserloot/lombok/issues/1563

Ответ 2

Еще один способ решить эту проблему. Используйте модуль имен параметров Джексона, который по умолчанию включен в весеннюю загрузку 2. После этого Джексон может десериализовать объекты. Но это работает, только если у вас есть более 1 свойства в объекте. В случае одного свойства я получаю следующее сообщение об ошибке:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of 'SomeClassName' (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

Из-за следующего:

Маркерная аннотация, которая может использоваться для определения конструкторов и фабричных методов как единица, используемая для создания новых экземпляров связанного класса.

ПРИМЕЧАНИЕ. При аннотировании методов-создателей (конструкторов, фабричных методов), метод должен быть либо:

  • Метод конструктора/фабрики с одним аргументом без аннотации JsonProperty для аргумента: если это так, то это так называемый "создатель делегата", и в этом случае Джексон сначала связывает JSON с типом аргумента, а затем вызывает создателя. Это часто используется в сочетании с JsonValue (используется для сериализации).
  • Метод конструктора/фабрики, где каждый аргумент аннотируется либо JsonProperty, либо JacksonInject, чтобы указать имя свойства для привязки к

Также обратите внимание, что все аннотации JsonProperty должны указывать фактическое имя (НЕ пустая строка для "по умолчанию"), если вы не используете один из модулей расширения, которые могут определять имя параметра; это потому, что версии JDK по умолчанию до 8 не смогли сохранить и/или получить имена параметров из байт-кода. Но в JDK 8 (или с использованием вспомогательных библиотек, таких как Paranamer, или других языков JVM, таких как Scala или Kotlin), указывать имя необязательно.

Чтобы справиться с этим делом с помощью Lombok, я использовал следующий обходной путь:

@Value
@AllArgsConstructor(onConstructor = @__(@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)))
class SomeClassName {...}

Ответ 3

Я обновил версию lombok до: 'org.projectlombok: lombok: 1.18.0', и это сработало для меня.