После обновления происходит сбой десериализации.
Я обновил свой микросервис с 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]




На мой взгляд, использование аннотации @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. Это сохранит имена параметров конструкторов и методов в сгенерированных файлах классов, чтобы их можно было получить через отражение.
Спасибо. Первый вариант сработал. А вот второй не сработал.
Объявление 2) Вы добавили lombok.config в корневую папку вашего проекта? Также обратите внимание, что после этого вам необходимо очистить и перекомпилировать.
@JanRieke - Не могли бы вы помочь мне в этом связанном вопросе - stackoverflow.com/questions/56245674/…
NoArgsConstructor не только потеряет неизменяемость, но и не сможет проверить значение null.
В моем случае Решение 1 было недостаточно, пришлось добавить еще и @AllArgsConstructor, чтобы избавиться от очередной ошибки конструктор ... в классе ... не может применяться к данным типам. Использование Java 1.8.0_265, Lombok 1.18.4, Jackson 2.9.5
Вы хотите десериализовать класс с конечным полем. поэтому вам нужно объявить конструктор, который содержит конечное поле для десериализации.
@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, это много шаблонных аннотаций для библиотеки, которая намеревается удалить шаблон.
@ChristopheBouhier Большая часть аннотаций уровня класса может быть настроена один раз при создании объекта Jackson ObjectMapper, я полагаю, они были включены здесь для демонстрационных целей ;-)
Фактически все это собрано в единую аннотацию: @Jacksonizedprojectlombok.org/features/experimental/Jacksonized
Решение 4
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() {
}
}
Вы пробовали добавить конструктор в класс ErrorDetail?