如何使用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
相关产品推荐
相关产品推荐

