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

@Controller接收Map<String,Object>时自定义类反序列化问题如何解决

反序列化问题解决方案

问题根因

你在实体类上添加的@JsonTypeInfo仅在Jackson反序列化时明确知道目标类型为该实体类/其父类/其实现的接口时才会生效。当前控制器入参定义为Map<String, Object>,值的目标类型是通用Object,Jackson默认不会扫描匹配带@JsonTypeInfo注解的实体类,遇到JSON对象会直接反序列化为LinkedHashMap,因此注解不会触发生效。

可落地解决方案

方案1:入参保持Map结构,手动转换复杂类型

无需调整入参定义,直接在业务逻辑中对Map中的复杂类型做转换:

@Autowired
private ObjectMapper objectMapper;

@PostMapping("/complete")
public ResponseEntity<?> complete(
    @RequestBody(required = false) Map<String, Object> variables
) {
    if (variables != null) {
        variables.replaceAll((k, v) -> {
            // 仅处理反序列化生成的LinkedHashMap类型
            if (v instanceof Map) {
                Map<String, Object> valueMap = (Map<String, Object>) v;
                String className = (String) valueMap.get("class");
                if (className == null) {
                    return v;
                }
                try {
                    // 白名单校验,避免恶意类实例化风险
                    if (!List.of("com.yourpackage.PurchaseOrder", "com.yourpackage.PurchaseOrderEntry")
                            .contains(className)) {
                        return v;
                    }
                    Class<?> targetClazz = Class.forName(className);
                    return objectMapper.convertValue(valueMap, targetClazz);
                } catch (ClassNotFoundException e) {
                    // 自定义异常处理逻辑
                    return v;
                }
            }
            // Integer、String等基础类型直接返回
            return v;
        });
    }
    // 后续业务逻辑
    return ResponseEntity.ok().build();
}

方案2:改用JsonNode作为入参,自行处理不同类型

JsonNode是Jackson提供的JSON树结构,可直接读取节点类型和属性,转换逻辑更清晰:

@Autowired
private ObjectMapper objectMapper;

@PostMapping("/complete")
public ResponseEntity<?> complete(
    @RequestBody(required = false) JsonNode variables
) {
    if (variables != null && variables.isObject()) {
        Iterator<Map.Entry<String, JsonNode>> fieldIter = variables.fields();
        while (fieldIter.hasNext()) {
            Map.Entry<String, JsonNode> field = fieldIter.next();
            JsonNode node = field.getValue();
            if (node.isTextual()) {
                String strVal = node.asText();
                // 处理字符串类型
            } else if (node.isInt() || node.isLong()) {
                long numVal = node.asLong();
                // 处理数字类型
            } else if (node.isObject()) {
                String className = node.get("class").asText();
                // 同样先做白名单校验
                Class<?> targetClazz = Class.forName(className);
                Object entity = objectMapper.treeToValue(node, targetClazz);
                // 处理自定义实体类
            }
        }
    }
    return ResponseEntity.ok().build();
}

方案3:全局配置ObjectMapper启用默认多态处理(仅限内网可信场景)

如果接口仅在内网提供、调用方完全可信,可以全局配置Jackson对所有非基础类型启用多态反序列化,无需手动转换:

@Configuration
public class JacksonConfig {
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        // 仅对非最终类型启用多态类型信息,和你实体类的配置规则匹配
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, 
                ObjectMapper.DefaultTyping.NON_FINAL, 
                JsonTypeInfo.As.PROPERTY);
        return mapper;
    }
}

注意:该配置存在远程代码执行(RCE)安全风险,公网暴露的接口禁止使用。

安全提示

无论使用哪种方案,都建议对请求中传入的class属性做白名单校验,仅允许你预设的自定义类通过校验,避免恶意构造的请求传入危险类触发漏洞。

内容的提问来源于stack exchange,提问作者aeowlian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 04:24:03