Jackson泛型反序列化报错:解析子类型缺少type id,如何通过反射注册子类型
解决方案
方案1:手动注册子类型(子类较少时使用)
直接调用Jackson的registerSubtypes方法手动注册所有Link的子类,无需依赖注解扫描:
ObjectMapper jacksonMapper = new ObjectMapper(); // 注册所有Link子类,有多个则依次传入 jacksonMapper.registerSubtypes(ProductOrder.class); // 若需要和注解中定义的子类型名称对齐,可使用NamedType配置 // jacksonMapper.registerSubtypes(new NamedType(ProductOrder.class, "ProductOrder"));
方案2:反射自动扫描注册子类型(子类较多时使用)
Spring Boot环境实现
直接用Spring自带的类扫描工具扫描指定包下所有Link的子类,批量注册到ObjectMapper:
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.beans.factory.config.BeanDefinition; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.NamedType; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.xxx.digital.ms.aaa.bbb.ck.resources.models.Link; import java.util.Set; // 初始化类扫描器,禁用默认的@Component等注解过滤规则 ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); // 只保留继承自Link的类 scanner.addIncludeFilter(new AssignableTypeFilter(Link.class)); // 扫描子类所在的包路径,替换成你自己的业务包路径 Set<BeanDefinition> subTypeBeans = scanner.findCandidateComponents("com.xxx.digital.ms.aaa.bbb.ck.resources.models"); ObjectMapper jacksonMapper = new ObjectMapper(); for (BeanDefinition beanDefinition : subTypeBeans) { Class<?> subType = Class.forName(beanDefinition.getBeanClassName()); // 排除父类Link本身,只注册子类 if (!Link.class.equals(subType)) { // 优先读取类上@JsonSubTypes.Type注解配置的name,没有则用默认类名 JsonSubTypes.Type typeAnno = subType.getAnnotation(JsonSubTypes.Type.class); if (typeAnno != null) { jacksonMapper.registerSubtypes(new NamedType(subType, typeAnno.name())); } else { jacksonMapper.registerSubtypes(subType); } } }
如果你的项目使用Spring Boot自动装配的全局ObjectMapper,可以通过Jackson2ObjectMapperBuilderCustomizer全局配置,避免每次new新的Mapper:
@Configuration public class JacksonConfig { @Bean public Jackson2ObjectMapperBuilderCustomizer registerLinkSubTypes() { return builder -> { // 把上面的扫描注册逻辑放到这里,注册到全局mapper }; } }
非Spring环境实现
可以使用自定义反射工具类扫描包下所有类,筛选出Link的子类后注册即可,逻辑和Spring版本一致,只需替换类扫描实现。
额外优化建议
你当前的使用场景中,foo()方法返回的ResponseEntity的Body本身已经是List<ProductOrder>类型,无需走convertValue做转换,可以直接类型判断后强转,完全规避多态类型检查问题:
Object body = ((ResponseEntity) retval).getBody(); if (body instanceof List<?> list) { if (!list.isEmpty() && list.get(0) instanceof ProductOrder) { List<ProductOrder> orderList = (List<ProductOrder>) list; // 直接使用orderList即可 } }
内容的提问来源于stack exchange,提问作者Tal Avissar
相关产品推荐
相关产品推荐

