You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 12:15:01