找回密码
 立即注册
首页 业界区 业界 Spring Boot中HTTP请求参数转换和请求体JSON反序列化的 ...

Spring Boot中HTTP请求参数转换和请求体JSON反序列化的区别

恿深疏 13 小时前
Spring Boot中HTTP请求参数转换和请求体JSON反序列化的区别

问题

假设如下方法和对象
  1. @Operation(summary = "新增或修改标签信息")
  2. @PostMapping("saveOrUpdate")
  3. public Result saveOrUpdateLabel(@RequestBody LabelInfo labelInfo) {
  4.     service.saveOrUpdate(labelInfo);
  5.     return Result.ok();
  6. }
  7. @Schema(description = "标签信息表")
  8. @TableName(value = "label_info")
  9. @Data
  10. public class LabelInfo extends BaseEntity {
  11.     private static final long serialVersionUID = 1L;
  12.     @Schema(description = "类型")
  13.     @TableField(value = "type")
  14.     private ItemType type;
  15.     @Schema(description = "标签名称")
  16.     @TableField(value = "name")
  17.     private String name;
  18. }
  19. @Getter
  20. @AllArgsConstructor
  21. public enum ItemType implements BaseEnum {
  22.     APARTMENT(1, "公寓"),
  23.     ROOM(2, "房间");
  24.     @EnumValue
  25.     @JsonValue
  26.     private Integer code;
  27.     private String name;
  28. }
复制代码
在saveOrUpdateLabel方法中,如果不对ItemType对象的code变量添加@JsonValue注解,若前端发送{ "type": "2",  "name": "两室一厅" }会报错
  1. Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `com.sense.lease.model.enums.ItemType` from String "2": not one of the values accepted for Enum class: [APARTMENT, ROOM]]
复制代码
此时ItemType的反序列化流程是什么样的?以及为什么是通过Jackson的@JsonValue实现反序列化而不是WebDataBinder的Converter?
1. 两种不同的转换场景

首先,我们需要区分两种不同的转换场景:

  • HTTP请求参数转换

    • 场景:URL查询参数或表单数据,如?type=2
    • 处理器:Spring MVC的WebDataBinder和Converter接口
    • 转换方向:String → Java对象
      
  • JSON反序列化

    • 场景:请求体中的JSON数据,如{"type": "2", "name": "两室一厅"}
    • 处理器:Jackson库的ObjectMapper
    • 转换方向:JSON字符串 → Java对象
      
2. JSON反序列化流程详解

当Spring Boot接收到包含JSON的请求体时,会发生以下过程:

  • HTTP请求到达
    1. POST /admin/label/saveOrUpdate
    2. Content-Type: application/json
    3. {
    4.   "type": "2",
    5.   "name": "两室一厅"
    6. }
    复制代码

  • Jackson处理JSON

    • Spring Boot使用Jackson的ObjectMapper来解析JSON
    • 当遇到"type": "2"时,Jackson需要将其转换为ItemType枚举
      
  • 默认枚举反序列化

    • Jackson默认使用枚举的名称进行反序列化
    • 它会尝试匹配"2"与枚举常量名称:[APARTMENT, ROOM]
    • 由于没有名为"2"的枚举常量,所以抛出异常
      
3. @JsonValue注解的作用

@JsonValue注解告诉Jackson在进行序列化和反序列化时,应该使用哪个字段作为枚举的值:
  1. public enum ItemType implements BaseEnum {
  2.     APARTMENT(1, "公寓"),
  3.     ROOM(2, "房间");
  4.     @EnumValue  // MyBatis-Plus使用此注解确定存储到数据库的值
  5.     @JsonValue  // Jackson使用此注解确定序列化/反序列化的值
  6.     private Integer code;
  7.    
  8.     // ...
  9. }
复制代码
当添加了@JsonValue注解后:

  • 序列化:将ItemType.ROOM转换为JSON时,输出2而不是"ROOM"
  • 反序列化:将JSON中的字符串"2"或者数字2转换为ItemType.ROOM对象
4. 为什么Converter不适用于JSON反序列化

Converter接口(如StringToItemTypeConverter)是为Spring MVC的WebDataBinder设计的,专门用于处理HTTP请求参数的转换,而不是JSON数据的反序列化。
两者的工作层面不同:

  • Converter

    • 工作在Spring MVC层面
    • 处理HTTP请求参数(查询参数、表单数据)
    • 在控制器方法参数绑定之前执行
      
  • Jackson的反序列化

    • 工作在JSON处理层面
    • 处理请求体中的JSON数据
    • 在控制器方法参数绑定之前执行,但独立于Spring MVC的转换机制
      
5. 验证这个区别

可以通过以下方式验证这个区别:

  • 测试请求参数(使用Converter):
    1. GET /admin/label/list?type=2
    复制代码
    这个请求会使用StringToItemTypeConverter进行转换

  • 测试JSON请求体(使用@JsonValue):
    1. POST /admin/label/saveOrUpdate
    2. Content-Type: application/json
    3. {
    4.   "type": "2",
    5.   "name": "两室一厅"
    6. }
    复制代码
    这个请求会使用Jackson的反序列化机制,依赖于@JsonValue注解

6. 替代方案:@JsonCreator注解

除了使用@JsonValue,您还可以使用@JsonCreator注解提供自定义的反序列化方法:
  1. // @JsonCreator注解指定自定义反序列化方法,静态工厂函数,根据code属性值返回对应的枚举对象实例
  2. // 参数类型需与Json数据中code属性的数据类型一致,或者声明为Object类型,否则无法将json数据与参数绑定
  3. // 参数名可直接使用Json数据中的属性名,否则需使用@JsonProperty注解指定属性名。
  4. @JsonCreator
  5. public static ItemType forValue(@JsonProperty("code") String c) {
  6.     for (ItemType type : ItemType.values()) {
  7.         if (type.getCode().equals(Integer.valueOf(c))) {
  8.             return type;
  9.         }
  10.     }
  11.     throw new IllegalArgumentException("code: " + c + "非法");
  12. }
复制代码
优点:灵活,可进行额外的处理流程
缺点

  • 繁琐,需要为每个枚举类单独实现
  • 需要注意参数的变量类型和变量名与JSON数据是否对应,否则无法触发;而@JsonValue注解时,JSON数据既可以是字符串也可以是数字
总结



  • HTTP请求参数转换:使用Converter接口,由Spring MVC的WebDataBinder处理
  • JSON反序列化:使用Jackson的机制,依赖于@JsonValue或@JsonCreator注解,不仅是请求体中的JSON数据反序列化,任何时候涉及到JSON数据反序列化,只要使用的是Spring Boot默认的ObejctMapper或配置了相同Jackson设置的ObejctMapper,相关注解都会生效。
  • 两者是独立的机制,解决不同场景下的类型转换问题
  • 为了完整支持两种场景,需要同时提供Converter和适当的Jackson注解

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册