it-roy-ru.com

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, которая генерирует конструктор. Это означает, что этот класс не имеет конструктора без аргументов.

Я использовал Spring Boot 1.4.3.RELEASE, и компонент ObjectMapper мог десериализовать такой объект из 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? Можно ли настроить бин так, чтобы он вел себя так же, как и в старой версии?

15
solomkinmv

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

lombok.anyConstructor.addConstructorProperties=true

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

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

Возможно, вас заинтересует следующая проблема GitHub: https://github.com/rzwitserloot/lombok/issues/1563

23
Tobias

Еще один способ решить эту проблему. Используйте Jackson модуль имен параметров , который включен в весеннюю загрузку 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 {...}
1
solomkinmv

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

0
fascynacja