Lombok 1.18.0 и Jackson 2.9.6 не работают вместе

После обновления происходит сбой десериализации.

Я обновил свой микросервис с Spring 1.5.10.RELEASE до Spring 2.0.3.RELEASE, а также обновил lombok с 1.16.14 до 1.18.0 и jackson-datatype-jsr310 с 2.9.4 на 2.9.6.

Строка JSON -

{"heading":"Validation failed","detail":"field must not be null"}

Класс -

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorDetail {

   private final String heading;
   private final String detail;
   private String type;
}

Вызов метода -

ErrorDetail errorDetail = asObject(jsonString, ErrorDetail.class);

Метод, используемый для десериализации -

import com.fasterxml.jackson.databind.ObjectMapper;
// more imports and class defination.

private static <T> T asObject(final String str, Class<T> clazz) {
    try {
        return new ObjectMapper().readValue(str, clazz);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Ошибка -

java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.foo.bar.ErrorDetail` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"heading":"Validation failed","detail":"field must not be null"}"; line: 1, column: 2]

Вы пробовали добавить конструктор в класс ErrorDetail?

Tugrul 22.07.2018 13:50
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
30
1
23 693
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

На мой взгляд, использование аннотации @Data - плохой подход. Пожалуйста, замените @Data на @Getting, @Setter, @EqualsAndHashcode и т. д.

и напишите сюда пожалуйста, если поможет.

Обновить

Я предлагаю, чтобы @Data создавал @RequiredArgsConstructor, и это конструктор с конечными полями и без private String type;

Ответ принят как подходящий

Lombok прекратил генерировать @ConstructorProperties в конструкторах с версией 1.16.20 (см. журнал изменений), потому что это могло нарушить работу приложений Java 9+, использующих модули. Эта аннотация содержит имена параметров конструктора (они удаляются при компиляции класса, так что это обходной путь, чтобы имена параметров по-прежнему можно было получить во время выполнения). Поскольку аннотация теперь не создается по умолчанию, Джексон не может сопоставить имена полей с параметрами конструктора.

Решение 1: Используйте @NoArgsConstructor и @Setter, но вы потеряете неизменность (если это важно для вас).

Обновлять: Только @NoArgsConstructor и @Getter (без @Setter) также могут работать (потому что INFER_PROPERTY_MUTATORS=true). Таким образом, вы можете сохранить неизменяемость класса, по крайней мере, из обычного (неотражающего) кода.

Решение 2: Настройте lombok для повторной генерации аннотаций, используя файл lombok.config, содержащий строку lombok.anyConstructor.addConstructorProperties = true. (Если вы используете модули, убедитесь, что java.desktop находится в пути к вашему модулю.) Очистите и перекомпилируйте после добавления файла lombok.config.

Решение 3: Используйте поддержку разработчика Jackson в сочетании с @Builder lombok, как описано здесь, или в @Randakar ответ, чтобы ответить на этот вопрос.

Решение 4: При компиляции с помощью javac (Java 8 и выше) добавьте к команде -parameters. Это сохранит имена параметров конструкторов и методов в сгенерированных файлах классов, чтобы их можно было получить через отражение.

Спасибо. Первый вариант сработал. А вот второй не сработал.

JHS 22.07.2018 15:02

Объявление 2) Вы добавили lombok.config в корневую папку вашего проекта? Также обратите внимание, что после этого вам необходимо очистить и перекомпилировать.

Jan Rieke 22.07.2018 16:04

@JanRieke - Не могли бы вы помочь мне в этом связанном вопросе - stackoverflow.com/questions/56245674/…

MasterJoe 21.05.2019 22:14

NoArgsConstructor не только потеряет неизменяемость, но и не сможет проверить значение null.

jchnxu 13.01.2020 01:26

В моем случае Решение 1 было недостаточно, пришлось добавить еще и @AllArgsConstructor, чтобы избавиться от очередной ошибки конструктор ... в классе ... не может применяться к данным типам. Использование Java 1.8.0_265, Lombok 1.18.4, Jackson 2.9.5

Marco Lackovic 25.09.2020 15:37

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

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorDetail {

private final String heading;
private final String detail;
private String type;

@JsonCreator
public ErrorDetail(@JsonProperty("heading") String heading, @JsonProperty("detail") String detail) {
    this.heading = heading;
    this.detail = detail;
}
}

а при десериализации с помощью маппера необходимо для MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS установить это свойство ложный.

private static <T> T asObject(final String str, Class<T> clazz) {
    try {
        return new ObjectMapper().configure(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS,false).readValue(str, clazz);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Обновлено: этот ответ теперь немного устарел: есть новая аннотация @Jacksonized от https://projectlombok.org/features/experimental/Jacksonized, которая заботится о большей части шаблона в этом ответе.


Лучший способ заставить Джексона и ломбока хорошо играть вместе - это всегда делать ваш DTO неизменяемым и говорить Джексону использовать конструктор для десериализации в ваши объекты.

Неизменяемые объекты - хорошая идея по той простой причине, что, когда поля не могут быть изменены, компиляторы на месте могут выполнять гораздо более агрессивную оптимизацию.

Для этого вам понадобятся две аннотации: JsonDeserialize и JsonPojoBuilder.

Пример:

@Builder
@Value // instead of @Data
@RequiredArgsConstructor
@NonNull // Best practice, see below.
@JsonDeserialize(builder = ErrorDetail.ErrorDetailBuilder.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorDetail {

   private final String heading;

   // Set defaults if fields can be missing, like this:
   @Builder.Default
   private final String detail = "default detail";

   // Example of how to do optional fields, you will need to configure
   // your object mapper to support that and include the JDK 8 module in your dependencies..
   @Builder.Default
   private Optional<String> type = Optional.empty()

   @JsonPOJOBuilder(withPrefix = "")
   public static final class ErrorDetailBuilder {
   }
}

Pffew, это много шаблонных аннотаций для библиотеки, которая намеревается удалить шаблон.

Christophe Bouhier 04.02.2020 16:22

@ChristopheBouhier Большая часть аннотаций уровня класса может быть настроена один раз при создании объекта Jackson ObjectMapper, я полагаю, они были включены здесь для демонстрационных целей ;-)

paradoxon 02.04.2020 14:19

Фактически все это собрано в единую аннотацию: @Jacksonizedprojectlombok.org/features/experimental/Jacksonized

Randakar 15.10.2020 14:14

Решение 4

  • Напишите NoArgsConstructor самостоятельно. Это, по крайней мере, сработало для меня с ломбоком 1.18.8 и Джексоном 2.9.9
    @Builder
    @Getter
    @AllArgsConstructor
    public class EventDTO {

        private String id;
        private Integer isCancelled;

        private String recurringEventId;

        private String summary;
        private String description;
        private String location;
        private String startDateTime;
        private String endDateTime;

        /**
         * Make Jackson happy
         */
        public EventDTO() {
        }
    }

Другие вопросы по теме