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

如何使用Jackson注解实现Spring @RequestBody抽象类多态反序列化

Jackson多态反序列化实现方案

以下两种方案均可实现AbstractRequest反序列化时自动映射到对应子类:

方案1:显式类型字段映射(推荐,性能更稳定、可维护性更高)

步骤1:给抽象父类添加多态配置注解

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME, // 使用预设的逻辑名称关联子类
        include = JsonTypeInfo.As.PROPERTY, // 类型标识作为JSON的独立属性传递
        property = "requestType" // 类型标识的JSON字段名
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = ConcreteOneRequest.class, name = "CONCRETE_ONE"),
        @JsonSubTypes.Type(value = ConcreteTwoRequest.class, name = "CONCRETE_TWO")
})
public abstract class AbstractRequest {
    // 父类原有字段定义
}

步骤2:子类添加反序列化支持注解

因为使用了Lombok的@SuperBuilder,需要添加@Jacksonized注解让Jackson识别Builder构造逻辑(Lombok 1.18.14及以上版本支持):

@Value
@SuperBuilder
@Jacksonized
public ConcreteOneRequest extends AbstractRequest {
    String concreteOneUniqueField;
}

@Value
@SuperBuilder
@Jacksonized
public ConcreteTwoRequest extends AbstractRequest {
    String concreteTwoUniqueField;
}

调用示例

请求时传入对应requestType即可自动映射:

// 映射为ConcreteOneRequest的请求体
{
  "requestType": "CONCRETE_ONE",
  "concreteOneUniqueField": "测试值1",
  "父类字段1": "xxx"
}

// 映射为ConcreteTwoRequest的请求体
{
  "requestType": "CONCRETE_TWO",
  "concreteTwoUniqueField": "测试值2",
  "父类字段1": "xxx"
}

方案2:字段自动识别映射(无需额外传递类型字段)

如果前端无法新增requestType字段,可以通过自定义TypeIdResolver根据JSON独有字段自动识别子类:

步骤1:实现自定义类型识别解析器

import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.io.IOException;

public class AbstractRequestTypeResolver implements TypeIdResolver {
    @Override
    public void init(JavaType baseType) {}

    @Override
    public String idFromValue(Object value) {
        return value.getClass().getName();
    }

    @Override
    public String idFromValueAndType(Object value, Class<?> suggestedType) {
        return idFromValue(value);
    }

    @Override
    public String idFromBaseType() {
        return AbstractRequest.class.getName();
    }

    // 核心识别逻辑
    @Override
    public JavaType typeFromId(DatabindContext context, String id) throws IOException {
        ObjectNode jsonNode = context.getParser().readValueAsTree();
        // 存在concreteOneUniqueField则判定为ConcreteOneRequest
        if (jsonNode.has("concreteOneUniqueField")) {
            return context.constructType(ConcreteOneRequest.class);
        }
        // 存在concreteTwoUniqueField则判定为ConcreteTwoRequest
        if (jsonNode.has("concreteTwoUniqueField")) {
            return context.constructType(ConcreteTwoRequest.class);
        }
        throw new IllegalArgumentException("无法匹配AbstractRequest的对应子类");
    }

    @Override
    public JsonTypeInfo.Id getMechanism() {
        return JsonTypeInfo.Id.CUSTOM;
    }
}

步骤2:修改父类注解使用自定义解析器

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;

@JsonTypeInfo(
        use = JsonTypeInfo.Id.CUSTOM,
        include = JsonTypeInfo.As.EXISTING_PROPERTY,
        visible = true
)
@JsonTypeIdResolver(AbstractRequestTypeResolver.class)
public abstract class AbstractRequest {
    // 父类原有字段定义
}

调用示例

无需传入类型字段,直接传递业务字段即可:

{
  "concreteOneUniqueField": "测试值1",
  "父类字段1": "xxx"
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 06:06:08