#java #spring #spring-boot #jackson
#java #весна #весенняя загрузка #джексон
Вопрос:
Я работал над обновлением версии Spring Boot одного из моих микросервисов и наткнулся на странное поведение. У меня есть такой класс:
public class FilteredData {
private final List<ShipmentData> shipments;
public FilteredData(@JsonProperty("listShipments") List<ShipmentData> shipments) {
this.shipments = shipments;
}
public List<ShipmentData> getShipments() {
return shipments;
}
}
Поведение, которое я имел перед выполнением обновления, заключалось в том, что при десериализации имя listShipments
использовалось в объекте JSON для сопоставления со shipments
свойством класса Java. Однако при сериализации shipments
свойство будет записываться с именем shipments
, а не listShipments
.
Проблема в том, что теперь он использует имя listShipments
как при десериализации, так и при сериализации. Я не уверен, в какой момент эта проблема начала возникать, поскольку моя первоначальная версия Spring Boot была 1.5.7, и я медленно обновляю ее до 2.3.4. Но я считаю, что это начало происходить после версии 2.0.0.
Я не знаю, вызвано ли это каким-то внутренним изменением в автоконфигурации Джексона Spring Boot или изменением в самой библиотеке Jackson, но мне трудно отследить, что вызвало это и как это исправить.
РЕДАКТИРОВАТЬ: я заметил, что с последней версии Spring Boot 1 (1.5.22) до Spring Boot 2.0.0 была изменена младшая версия Jackson (с 2.8 до 2.9). Могло ли это вызвать проблему?
Ответ №1:
Я смог воспроизвести это точное поведение при переключении с Spring Boot 1.5.7.RELEASE на 2.0.0.RELEASE. Сначала я добавил родительский элемент и зависимость spring-boot-starter-web. Затем я создал простой @RestController
, использовал предоставленный вами POJO и ShipmentData
заменил String
его. Когда я вручную создаю Jackson ObjectMapper
, я не смог воспроизвести проблему в двух выпусках.
Самая интересная часть: когда вы переименовываете имя параметра конструктора во что-то другое shipments
, кажется, что оно работает нормально. Возможно, это может быть ошибка в Jackson или где-то в Spring Framework, которая проявляется только тогда, когда средство получения совпадает с именем параметра в конструкторе.
Я предлагаю использовать @JsonAlias
для определения альтернативного имени, которое должно быть принято во время десериализации:
@JsonCreator
public FilteredData(
@JsonProperty("shipments")
@JsonAlias("listShipments")
List<ShipmentData> shipments) {
this.shipments = shipments;
}
При этом вы можете десериализовать с помощью обоих listShipments
и shipments
в то время как используется только для сериализации shipments
(определяется getter).
Для справки, рабочая реализация с 2.3.3.RELEASE:
@RestController
public class FilteredDataController {
@PostMapping("/data")
public FilteredData postFilteredData(@RequestBody FilteredData data) {
return data;
}
}
public class FilteredData {
private final List<String> shipments;
@JsonCreator
public FilteredData(
@JsonProperty("shipments") @JsonAlias("listShipments")
List<String> shipments) {
this.shipments = shipments;
}
public List<String> getShipments() {
return shipments;
}
}
Запрос:
curl -d '{"listShipments":["a","b","c"]}' -H 'Content-Type: application/json' http://localhost:8080/data
Результат:
{"shipments":["a","b","c"]}
Ответ №2:
Мне удалось найти проблему! Это было вызвано зависимостью, используемой spring-boot-starter-web
called jackson-module-parameter-names
. Эта библиотека предоставляет модуль Jackson с именем ParameterNamesModule
, который, согласно автоконфигурации Jackson Spring Boot, автоматически импортирует любой модуль Jackson, найденный в пути к классу.
Описание этой библиотеки в https://github.com/FasterXML/jackson-modules-java8 говорит:
Имена параметров: поддержка определения параметров конструктора и фабричного метода («создателя») без использования аннотации @JsonProperty. Предоставляет com.fasterxml.jackson.module.paramnames.Параметрнаймодуль
Я все еще не уверен на 100%, почему это создало проблему, которую оно создало, но удаление ее с помощью Maven решило проблему:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</exclusion>
</exclusions>
</dependency>
Ответ №3:
Между Spring Boot 1 и Spring Boot 2 произошли серьезные изменения. Вы уже пробовали что-нибудь исправить? Например. пытались переместить аннотацию JsonProperty в поле, а не в конструктор? или используя @JsonCreator
онлайн-ресурс: https://www.baeldung.com/jackson-deserialize-immutable-objects
Комментарии:
1. Пробовал оба. Точно такое же поведение