Spring Boot接口作为请求体实现多态时的反序列化错误问题
解决Spring多态请求体反序列化问题
问题场景
需要处理两种不同格式的POST请求:
{ "notification": "order_creation_notification", "data": { "date": "22 Jan 2022" } }
{ "notification": "login_notification", "data": { "email": "johndoe@email.com", "at": "LA, USA" } }
对应的Controller和实体类代码如下:
Controller代码
@PostMapping(value="/") public void createNotification(@RequestBody NotificationRequest request) { // 业务逻辑 }
请求体类
@JsonTypeInfo(use = Id.DEDUCTION) @JsonSubTypes({ @Type(OrderCreationNotification.class), @Type(LoginNotification.class) }) public interface IData { }
@AllArgsConstructor @Getter @Setter public class OrderCreationNotification implements IData { private Date date; }
@AllArgsConstructor @Getter @Setter public class LoginNotification implements IData { private String email; private String at; }
@AllArgsConstructor @Getter @Setter public class NotificationRequest { private String notification; private IData data; }
发送请求时出现反序列化错误:
JSON parse error: Cannot construct instance of `LoginNotification` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `LoginNotification` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)\n at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 8, column: 13] (through reference chain: NotificationRequest[\"data\"])
解决方案
1. 修复构造器缺失问题
错误的直接原因是Jackson无法找到无参构造器来实例化实体类。虽然@AllArgsConstructor生成了全参构造,但Jackson默认需要无参构造完成对象实例化,后续再填充字段。
给所有IData的实现类添加无参构造器(使用Lombok的@NoArgsConstructor):
@NoArgsConstructor @AllArgsConstructor @Getter @Setter public class OrderCreationNotification implements IData { private Date date; }
@NoArgsConstructor @AllArgsConstructor @Getter @Setter public class LoginNotification implements IData { private String email; private String at; }
2. 优化多态反序列化配置(可选但推荐)
当前使用的Id.DEDUCTION是Jackson 2.12+的自动推导特性,依靠子类字段匹配来确定类型,存在字段重叠时的不确定性。更可靠的方式是利用外层的notification字段明确指定data的类型:
修改IData的注解配置:
@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "notification" ) @JsonSubTypes({ @Type(value = OrderCreationNotification.class, name = "order_creation_notification"), @Type(value = LoginNotification.class, name = "login_notification") }) public interface IData { }
EXTERNAL_PROPERTY:表示类型标识符来自外层对象的指定属性(即notification)name:指定每个子类对应的notification字段值,确保类型匹配精准
验证
修改完成后重新发送请求,Jackson将正确识别data的类型并完成反序列化,不再抛出错误。
内容的提问来源于stack exchange,提问作者StudentWhoCurious
相关产品推荐
相关产品推荐

