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', и это сработало для меня.