Ломбок 1.18.0 и Джексон 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]
3 ответа:
Ломбок перестал генерировать
@ConstructorProperties
на конструкторах с версией 1.16.20 (см. changelog ), потому что это могло бы сломать приложения Java 9+, которые используют модули. Эта аннотация содержит имена параметров конструктора (они удаляются при компиляции класса, так что это обходной путь, так что имена параметров все еще могут быть получены во время выполнения). Поскольку аннотации теперь не создаются по умолчанию, Джексон не может сопоставить имена полей конструктору параметры.Решение 1: Используйте
@NoArgsConstructor
и@Setter
, но вы потеряете неизменность (если это важно для вас).обновление: Просто
@NoArgsConstructor
и@Getter
(Без@Setter
) также может работать (потому чтоINFER_PROPERTY_MUTATORS=true
). Таким образом, вы можете сохранить класс неизменяемым, по крайней мере, из регулярного (неотражающего) кода.Решение 2: Настройте lombok для повторного создания аннотаций, используя файл
lombok.config
, содержащий строкуlombok.anyConstructor.addConstructorProperties = true
. (Если это так используя модули, убедитесь, чтоjava.desktop
находится на пути к модулю.)Решение 3: Используйте поддержку конструктора Джексона в сочетании с поддержкой Ломбока
@Builder
, как описано здесь.
Вы хотите десериализовать класс, который имеет конечное поле. поэтому вам нужно объявить конструктор, который содержит конечное поле для десериализации.
@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; } }
А при десериализации с помощью mapper нужно MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS Установите это свойство false.
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); } }
Использование аннотации
И напишите сюда, пожалуйста, если это поможет.@Data
, на мой взгляд, плохой подход. Пожалуйста, измените@Data
на@Getting
,@Setter
,@EqualsAndHashcode
и так далее ..Обновить
Предлагаю, чтобы
@Data
создавали@RequiredArgsConstructor
, а это конструктор с конечными полями, и безprivate String type
;