将JSON反序列化为Record时出现JSON解析错误
解决Java Record作为Spring MVC DTO的反序列化报错问题
问题场景
在Spring MVC控制器中使用TransactionDTO(Java Record)接收JSON请求体时,出现以下报错:
Could not resolve parameter [0] in private org.springframework.http.ResponseEntity<com.web.store.app.backend.dto.TransactionDTO> com.web.store.app.backend.controller.TransactionController.saveTransaction(com.web.store.app.backend.dto.TransactionDTO): JSON parse error: Can not set final java.util.List field com.web.store.app.backend.dto.TransactionDTO.productIds to java.util.ArrayList
将Record改为普通类并移除final修饰符后可正常运行,但希望继续使用Record作为DTO。
核心原因
Java Record的所有字段默认是final的,低版本Jackson(2.12以下)不支持通过Record的构造器完成反序列化,会尝试直接反射修改final字段,导致报错。另外,控制器方法使用private修饰符不符合Spring MVC规范,可能引发潜在问题。
解决方案
1. 修正控制器方法的访问权限
Spring MVC控制器方法必须为public,否则框架无法正确调用。修改控制器代码:
@PostMapping(value = "add") public ResponseEntity<TransactionDTO> saveTransaction(@Valid @RequestBody TransactionDTO transactionDTO) { return transactionService.saveTransaction(transactionDTO).map( transaction -> ResponseEntity.status(HttpStatus.CREATED).body(transaction)).orElseGet( () -> ResponseEntity.status(HttpStatus.BAD_REQUEST).build()); }
2. 升级Spring Boot版本(推荐)
Jackson从2.12版本开始正式支持Java Record的反序列化,对应Spring Boot 2.4及以上版本。直接升级Spring Boot版本到2.4+,Jackson会自动使用Record的构造器创建实例,无需额外配置。
3. 低版本环境下的兼容配置(无法升级时)
如果无法升级Spring Boot版本,可通过以下两种方式解决:
方式一:自定义Jackson ObjectMapper
添加配置类,启用Record支持:
@Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); // 处理日期时间类型 mapper.registerModule(new JavaTimeModule()); // 启用Record支持 mapper.enable(MapperFeature.SUPPORT_RECORD_GETTERS); // 全局配置蛇形命名(可根据需求选择) mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); return mapper; } }
方式二:为Record添加显式构造器
给Record添加带@JsonCreator注解的构造器,并为每个参数指定@JsonProperty:
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public record TransactionDTO(String id, Long userId, @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mmXXX", shape = JsonFormat.Shape.STRING) ZonedDateTime createdAt, List<String> productIds) { @JsonCreator public TransactionDTO( @JsonProperty("id") String id, @JsonProperty("user_id") Long userId, @JsonProperty("created_at") @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mmXXX", shape = JsonFormat.Shape.STRING) ZonedDateTime createdAt, @JsonProperty("product_ids") List<String> productIds) { this.id = id; this.userId = userId; this.createdAt = createdAt; this.productIds = productIds; } }
验证
使用原curl命令测试:
curl --location 'localhost:8080/transaction/api/add' \ --header 'Content-Type: application/json' \ --data '{ "id": "TRNS03", "user_id": 1, "created_at":"2023-03-19T06:33-06:00", "product_ids":["PRD003"] }'
内容的提问来源于stack exchange,提问作者Binary
相关产品推荐
相关产品推荐

