Spring Boot JSON 解析错误:HttpMessageNotreadableException
详解与解决方案
1. 错误概述
在 Spring Boot 应用中,当客户端发送 JSON 数据到后端接口时,可能会遇到以下错误:
org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: Cannot construct instance of `com.example.MyRequestVO`
(although at least one Creator exists):
no String-argument constructor/factory method to deserialize from String value ('{"key":"value"}')
这个错误表示 Spring 无法将传入的 JSON 数据正确转换成目标 Java 对象,通常是由于 JSON 反序列化失败导致的。
2. 错误原因分析
(1) 错误的请求参数接收方式
-
错误示例:使用
@RequestParam
或String
接收 JSON 数据
@PostMapping("/test")
public String test(@RequestParam String jsonData) { // ❌ 错误方式
// 尝试手动解析 JSON
}
- 这样会导致 Spring 把整个 JSON 当作一个字符串处理,而不是自动解析成对象。
正确方式:使用 @RequestBody
接收 JSON
@PostMapping("/test")
public String test(@RequestBody MyRequestVO request) { // ✅ 正确方式
// Spring 会自动解析 JSON 到 request 对象
}
(2) 目标类缺少无参构造方法
-
Jackson 反序列化要求:
- 如果类没有显式定义构造方法,Jackson 会使用默认的无参构造方法。
- 如果类有带参数的构造方法,但没有无参构造方法,且没有
@JsonCreator
标注,会导致反序列化失败。
-
错误示例:
public class MyRequestVO {
private String name;
private int age;
public MyRequestVO(String name, int age) { // ❌ 缺少无参构造方法
this.name = name;
this.age = age;
}
}
解决方案:
- 方法 1:添加无参构造方法
public MyRequestVO() {} // ✅ 添加无参构造方法
- 方法 2:使用
@JsonCreator
标注构造方法
@JsonCreator
public MyRequestVO(
@JsonProperty("name") String name,
@JsonProperty("age") int age
) {
this.name = name;
this.age = age;
}
(3) JSON 格式不正确
-
可能情况:
- JSON 数据格式错误(如缺少引号、多余的逗号)
- 字段名与 Java 类属性名不匹配(大小写、命名风格不同)
-
示例:
{
"userName": "John", // Java 类属性可能是 `username`
"age": 30
}
- 如果 Java 类使用
username
,但 JSON 使用userName
,会导致字段无法映射。
解决方案:
- 方法 1:使用
@JsonProperty
指定 JSON 字段名
public class MyRequestVO {
@JsonProperty("userName")
private String username;
}
- 方法 2:配置 Jackson 忽略大小写
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE);
}
}
(4) 目标类不可变(final)或缺少 setter 方法
-
Jackson 默认使用 setter 方法或字段反射赋值:
- 如果类属性是
final
的,或者没有setter
方法,会导致赋值失败。
- 如果类属性是
-
错误示例:
public class MyRequestVO {
private final String name; // ❌ final 字段无法赋值
private int age;
// 没有 setter 方法
}
解决方案:
- 方法 1:提供 setter 方法
public void setName(String name) {
this.name = name;
}
- 方法 2:使用
@JsonCreator
+@JsonProperty
(适用于不可变对象)
@JsonCreator
public MyRequestVO(
@JsonProperty("name") String name,
@JsonProperty("age") int age
) {
this.name = name;
this.age = age;
}
3. 解决方案总结
问题 | 解决方案 |
---|---|
错误使用 @RequestParam 接收 JSON | 改用 @RequestBody |
缺少无参构造方法 | 添加无参构造方法 或 使用 @JsonCreator |
JSON 字段名与 Java 属性名不匹配 | 使用 @JsonProperty 或 配置 Jackson 命名策略 |
类不可变(final)或缺少 setter | 提供 setter 或 使用 @JsonCreator 构造方法 |
JSON 格式错误 | 检查 JSON 数据是否合法 |
4. 最佳实践
(1) 推荐使用 @RequestBody
接收 JSON\
@PostMapping("/user")
public ResponseEntity<String> createUser(@RequestBody UserRequest request) {
// 处理逻辑
return ResponseEntity.ok("Success");
}
(2) 确保目标类可被反序列化
@Data // Lombok 自动生成 getter/setter
@NoArgsConstructor // 无参构造方法
@AllArgsConstructor // 全参构造方法
public class UserRequest {
private String username;
private int age;
}
(3) 使用 @JsonInclude
控制 JSON 行为
@JsonInclude(JsonInclude.Include.NON_NULL) // 忽略 null 字段
public class UserRequest {
private String username;
private Integer age;
}
(4) 全局 Jackson 配置(可选)
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
}
5. 总结
-
HttpMessageNotReadableException
通常是由于 JSON 反序列化失败导致的。 - 主要检查:
- 是否使用
@RequestBody
接收 JSON? - 目标类是否有无参构造方法或
@JsonCreator
? - JSON 字段名是否与 Java 属性匹配?
- 是否有 setter 方法或
final
限制?
- 是否使用
- 最佳实践:使用 Lombok (
@Data
+@NoArgsConstructor
) +@RequestBody
,避免手动解析 JSON。