Spring Boot中HTTP请求参数转换和请求体JSON反序列化的区别
问题假设如下方法和对象Operation(summary 新增或修改标签信息) PostMapping(saveOrUpdate) public Result saveOrUpdateLabel(RequestBody LabelInfo labelInfo) { service.saveOrUpdate(labelInfo); return Result.ok(); } Schema(description 标签信息表) TableName(value label_info) Data public class LabelInfo extends BaseEntity { private static final long serialVersionUID 1L; Schema(description 类型) TableField(value type) private ItemType type; Schema(description 标签名称) TableField(value name) private String name; } Getter AllArgsConstructor public enum ItemType implements BaseEnum { APARTMENT(1, 公寓), ROOM(2, 房间); EnumValue JsonValue private Integer code; private String name; }在saveOrUpdateLabel方法中如果不对ItemType对象的code变量添加JsonValue注解若前端发送{ type: 2, name: 两室一厅 }会报错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的Converter1. 两种不同的转换场景首先我们需要区分两种不同的转换场景HTTP请求参数转换场景URL查询参数或表单数据如?type2处理器Spring MVC的WebDataBinder和Converter接口转换方向String → Java对象JSON反序列化场景请求体中的JSON数据如{type: 2, name: 两室一厅}处理器Jackson库的ObjectMapper转换方向JSON字符串 → Java对象2. JSON反序列化流程详解当Spring Boot接收到包含JSON的请求体时会发生以下过程HTTP请求到达POST /admin/label/saveOrUpdate Content-Type: application/json { type: 2, name: 两室一厅 }Jackson处理JSONSpring Boot使用Jackson的ObjectMapper来解析JSON当遇到type: 2时Jackson需要将其转换为ItemType枚举默认枚举反序列化Jackson默认使用枚举的名称进行反序列化它会尝试匹配2与枚举常量名称[APARTMENT, ROOM]由于没有名为2的枚举常量所以抛出异常3. JsonValue注解的作用JsonValue注解告诉Jackson在进行序列化和反序列化时应该使用哪个字段作为枚举的值public enum ItemType implements BaseEnum { APARTMENT(1, 公寓), ROOM(2, 房间); EnumValue // MyBatis-Plus使用此注解确定存储到数据库的值 JsonValue // Jackson使用此注解确定序列化/反序列化的值 private Integer code; // ... }当添加了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. 验证这个区别可以通过以下方式验证这个区别测试请求参数使用ConverterGET /admin/label/list?type2这个请求会使用StringToItemTypeConverter进行转换测试JSON请求体使用JsonValuePOST /admin/label/saveOrUpdate Content-Type: application/json { type: 2, name: 两室一厅 }这个请求会使用Jackson的反序列化机制依赖于JsonValue注解6. 替代方案JsonCreator注解除了使用JsonValue您还可以使用JsonCreator注解提供自定义的反序列化方法// JsonCreator注解指定自定义反序列化方法静态工厂函数根据code属性值返回对应的枚举对象实例 // 参数类型需与Json数据中code属性的数据类型一致或者声明为Object类型否则无法将json数据与参数绑定 // 参数名可直接使用Json数据中的属性名否则需使用JsonProperty注解指定属性名。 JsonCreator public static ItemType forValue(JsonProperty(code) String c) { for (ItemType type : ItemType.values()) { if (type.getCode().equals(Integer.valueOf(c))) { return type; } } throw new IllegalArgumentException(code: c 非法); }优点灵活可进行额外的处理流程缺点繁琐需要为每个枚举类单独实现需要注意参数的变量类型和变量名与JSON数据是否对应否则无法触发而JsonValue注解时JSON数据既可以是字符串也可以是数字总结HTTP请求参数转换使用Converter接口由Spring MVC的WebDataBinder处理JSON反序列化使用Jackson的机制依赖于JsonValue或JsonCreator注解不仅是请求体中的JSON数据反序列化任何时候涉及到JSON数据反序列化只要使用的是Spring Boot默认的ObejctMapper或配置了相同Jackson设置的ObejctMapper相关注解都会生效。