Jackson多态类型反序列化:处理类名拼写错误与类型字段缺失的异常
解决Jackson多态类型处理的两个常见问题
我来帮你搞定这两个实际开发中经常碰到的Jackson多态解析问题,下面是具体的解决方案:
问题1:处理type字段拼写错误的自定义提示
当JSON里的type值拼写错误时,Jackson默认抛出的异常确实不够友好,咱们可以通过自定义类型解析器+全局异常处理来实现自定义提示:
自定义TypeIdResolver
继承Jackson的TypeIdResolverBase,重写resolveTypeId方法,当找不到匹配的类型时抛出咱们自己定义的异常:public class CustomPetTypeIdResolver extends TypeIdResolverBase { private JavaType baseType; @Override public void init(JavaType baseType) { this.baseType = baseType; } @Override public String idFromValue(Object value) { return value.getClass().getSimpleName(); } @Override public String idFromValueAndType(Object value, Class<?> suggestedType) { return idFromValue(value); } @Override public JavaType typeFromId(DatabindContext context, String id) throws IOException { // 匹配咱们的子类类型 switch (id) { case "Cat": return context.constructType(Cat.class); case "Dog": return context.constructType(Dog.class); default: // 抛出自定义异常 throw new InvalidPetTypeException("提供的类型错误"); } } @Override public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.NAME; } } // 自定义异常类 public class InvalidPetTypeException extends RuntimeException { public InvalidPetTypeException(String message) { super(message); } }在Pet类上指定自定义解析器
修改@JsonTypeInfo注解,添加resolver属性:@JsonTypeInfo(use = Id.NAME, property = "type", resolver = CustomPetTypeIdResolver.class) @JsonSubTypes({ @Type(value = Cat.class), @Type(value = Dog.class) }) @JsonIgnoreProperties(ignoreUnknown = true) public abstract class Pet { /* ... */ }全局异常捕获
用Spring的@RestControllerAdvice捕获自定义异常,返回友好提示:@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(InvalidPetTypeException.class) public ResponseEntity<String> handleInvalidPetType(InvalidPetTypeException e) { return ResponseEntity.badRequest().body(e.getMessage()); } }
这样当type值拼写错误(比如"Dogg")时,就会返回"提供的类型错误"的提示了。
问题2:处理type字段缺失的明确提示
对于type字段缺失的情况,有两种常用方案:
方案一:捕获Jackson的MissingTypeIdException
Jackson在找不到type字段时会抛出MissingTypeIdException,咱们直接在全局异常处理器里捕获它:
@RestControllerAdvice public class GlobalExceptionHandler { // 其他异常处理... @ExceptionHandler(MissingTypeIdException.class) public ResponseEntity<String> handleMissingTypeId(MissingTypeIdException e) { return ResponseEntity.badRequest().body("类型字段type不能为空"); } }
方案二:结合Bean Validation校验
如果你的项目用了Bean Validation(比如Hibernate Validator),可以给Pet类添加type属性并标记校验注解:
@JsonTypeInfo(use = Id.NAME, property = "type", resolver = CustomPetTypeIdResolver.class) @JsonSubTypes({ @Type(value = Cat.class), @Type(value = Dog.class) }) @JsonIgnoreProperties(ignoreUnknown = true) @Data // 用Lombok的@Data简化getter/setter,或者手动实现 public abstract class Pet { @NotEmpty(message = "类型字段type不能为空") private String type; // 其他属性和方法... }
然后在Controller的参数上加上@Valid注解触发校验:
@PostMapping("/pets") public ResponseEntity<String> addPet(@Valid @RequestBody Pet pet) { // 业务逻辑... return ResponseEntity.ok("添加成功"); }
这时如果请求里没有type字段,Validation会抛出ConstraintViolationException,咱们同样在全局异常处理器里捕获它,返回自定义提示:
@ExceptionHandler(ConstraintViolationException.class) public ResponseEntity<String> handleConstraintViolation(ConstraintViolationException e) { String message = e.getConstraintViolations().stream() .map(ConstraintViolation::getMessage) .findFirst() .orElse("请求参数错误"); return ResponseEntity.badRequest().body(message); }
两种方案都能实现明确的异常提示,你可以根据项目的实际情况选择。
内容的提问来源于stack exchange,提问作者3AKOBAH
相关产品推荐
相关产品推荐

