Jackson 在构造函数中使用 @JsonProperty 名称进行序列化
Jackson serializes using @JsonProperty name in constructor
我一直在努力升级我的一个微服务的 Spring 启动版本,我偶然发现了一个奇怪的行为。我有 class 这样的:
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 class。但是,在序列化时,它会使用名称 shipments
而不是 listShipments
写入 shipments
属性。
问题是现在它在反序列化和序列化时都使用名称 listShipments
。我不确定这个问题是从什么时候开始发生的,因为我最初的 Spring 引导版本是 1.5.7,我正在慢慢升级到 2.3.4。但我相信它是在 2.0.0 版本之后开始发生的。
我不知道这是由于 Spring Boot 的 Jackson 自动配置中的某些内部更改引起的,还是实际 Jackson 库中的更改引起的,但我很难追踪导致此问题的原因和如何修复它。
编辑:我注意到从最新的 Spring Boot 1 版本 (1.5.22) 到 Spring Boot 2.0.0,Jackson 次要版本被提升了(从 2.8 到 2.9)。这会导致问题吗?
Spring Boot 1 和 Spring Boot 2 之间发生了重大变化。您是否已尝试修复它?例如。试图将 JsonProperty 注释移动到字段而不是构造函数?或者使用@JsonCreator
在线资源:https://www.baeldung.com/jackson-deserialize-immutable-objects
从 Spring Boot 1.5.7.RELEASE 切换到 2.0.[=53 时,我能够重现这个确切的行为=]。首先,我添加了父项和 spring-boot-starter-web 依赖项。然后,我创建了一个简单的 @RestController
,使用您提供的 POJO 并将 ShipmentData
替换为 String
。当我手动创建 Jackson ObjectMapper
时,我无法在两个版本中重现该问题。
最有趣的部分:当您将构造函数的参数名称重命名为 shipments
以外的名称时,它似乎工作正常。这可能是 Jackson 或 Spring 框架中某处的错误,只有当 getter 与构造函数中的参数名称匹配时才会出现。
我建议使用@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"]}
我找到问题了!这是由 spring-boot-starter-web
使用名为 jackson-module-parameter-names
的依赖项引起的。这个库提供了一个名为 ParameterNamesModule
的 Jackson 模块,根据 Spring Boot 的 Jackson 自动配置,在类路径中找到的任何 Jackson 模块都会自动导入。
https://github.com/FasterXML/jackson-modules-java8 中对该库的描述说:
Parameter names: support for detecting constructor and factory method ("creator") parameters without having to use @JsonProperty annotation. Provides com.fasterxml.jackson.module.paramnames.ParameterNamesModule
我仍然不是 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>
我一直在努力升级我的一个微服务的 Spring 启动版本,我偶然发现了一个奇怪的行为。我有 class 这样的:
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 class。但是,在序列化时,它会使用名称 shipments
而不是 listShipments
写入 shipments
属性。
问题是现在它在反序列化和序列化时都使用名称 listShipments
。我不确定这个问题是从什么时候开始发生的,因为我最初的 Spring 引导版本是 1.5.7,我正在慢慢升级到 2.3.4。但我相信它是在 2.0.0 版本之后开始发生的。
我不知道这是由于 Spring Boot 的 Jackson 自动配置中的某些内部更改引起的,还是实际 Jackson 库中的更改引起的,但我很难追踪导致此问题的原因和如何修复它。
编辑:我注意到从最新的 Spring Boot 1 版本 (1.5.22) 到 Spring Boot 2.0.0,Jackson 次要版本被提升了(从 2.8 到 2.9)。这会导致问题吗?
Spring Boot 1 和 Spring Boot 2 之间发生了重大变化。您是否已尝试修复它?例如。试图将 JsonProperty 注释移动到字段而不是构造函数?或者使用@JsonCreator
在线资源:https://www.baeldung.com/jackson-deserialize-immutable-objects
从 Spring Boot 1.5.7.RELEASE 切换到 2.0.[=53 时,我能够重现这个确切的行为=]。首先,我添加了父项和 spring-boot-starter-web 依赖项。然后,我创建了一个简单的 @RestController
,使用您提供的 POJO 并将 ShipmentData
替换为 String
。当我手动创建 Jackson ObjectMapper
时,我无法在两个版本中重现该问题。
最有趣的部分:当您将构造函数的参数名称重命名为 shipments
以外的名称时,它似乎工作正常。这可能是 Jackson 或 Spring 框架中某处的错误,只有当 getter 与构造函数中的参数名称匹配时才会出现。
我建议使用@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"]}
我找到问题了!这是由 spring-boot-starter-web
使用名为 jackson-module-parameter-names
的依赖项引起的。这个库提供了一个名为 ParameterNamesModule
的 Jackson 模块,根据 Spring Boot 的 Jackson 自动配置,在类路径中找到的任何 Jackson 模块都会自动导入。
https://github.com/FasterXML/jackson-modules-java8 中对该库的描述说:
Parameter names: support for detecting constructor and factory method ("creator") parameters without having to use @JsonProperty annotation. Provides com.fasterxml.jackson.module.paramnames.ParameterNamesModule
我仍然不是 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>