在Java开发中,我们经常会定义父类和多个子类,在业务场景中父类类型的属性可能实际存储的是不同子类的实例,当使用Jackson库将JSON字符串反序列化为这类对象时,默认的逻辑只会按照父类的结构解析,子类的特有属性会被忽略,甚至可能出现类型转换错误。要解决这个问题,就需要用到Jackson的多态类型反序列化相关配置。

核心注解配置方式
Jackson提供了@JsonTypeInfo和@JsonSubTypes两个核心注解,用来声明多态类型的识别规则,直接在父类上添加这两个注解即可让反序列化时正确识别子类类型。
注解参数说明
@JsonTypeInfo:用来指定类型识别的方式,常用属性包括use(识别类型的方式,比如类名、自定义标识)、include(类型标识存放的位置,比如属性中、包装层)、property(类型标识对应的属性名)@JsonSubTypes:用来声明父类对应的所有子类,以及每个子类对应的类型标识值
代码示例
首先定义父类和两个子类:
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
// 父类添加多态类型注解
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME, // 使用自定义名称作为类型标识
include = JsonTypeInfo.As.PROPERTY, // 类型标识作为JSON的一个属性
property = "type" // 类型标识的属性名为type
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"), // 子类Dog对应标识dog
@JsonSubTypes.Type(value = Cat.class, name = "cat") // 子类Cat对应标识cat
})
class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Dog extends Animal {
private String barkSound; // 子类特有属性
public String getBarkSound() {
return barkSound;
}
public void setBarkSound(String barkSound) {
this.barkSound = barkSound;
}
}
class Cat extends Animal {
private String meowSound; // 子类特有属性
public String getMeowSound() {
return meowSound;
}
public void setMeowSound(String meowSound) {
this.meowSound = meowSound;
}
}
然后定义包含父类属性的容器类:
class AnimalContainer {
private Animal animal; // 父类属性,实际可能是Dog或Cat实例
public Animal getAnimal() {
return animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
}
反序列化测试代码:
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonPolymorphicTest {
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
// 子类Dog的JSON字符串,包含type标识和子类特有属性
String dogJson = "{"animal":{"type":"dog","name":"小黑","barkSound":"汪汪汪"}}";
AnimalContainer dogContainer = objectMapper.readValue(dogJson, AnimalContainer.class);
// 反序列化后animal实际是Dog实例,可以强转获取子类属性
Dog dog = (Dog) dogContainer.getAnimal();
System.out.println("Dog name: " + dog.getName() + ", bark: " + dog.getBarkSound());
// 子类Cat的JSON字符串
String catJson = "{"animal":{"type":"cat","name":"小白","meowSound":"喵喵喵"}}";
AnimalContainer catContainer = objectMapper.readValue(catJson, AnimalContainer.class);
Cat cat = (Cat) catContainer.getAnimal();
System.out.println("Cat name: " + cat.getName() + ", meow: " + cat.getMeowSound());
}
}
运行上述代码可以正确输出子类的特有属性,说明反序列化时已经正确识别了父类属性对应的子类实例。
自定义类型解析器方式
如果子类的类型标识不是固定的名称,或者需要根据复杂的规则判断类型,可以使用自定义类型解析器,实现TypeIdResolver接口即可。
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
// 自定义类型解析器
class CustomAnimalTypeIdResolver extends TypeIdResolverBase {
private JavaType baseType;
private Map<String, Class<?>> typeMap = new HashMap<>();
private Map<Class<?>, String> idMap = new HashMap<>();
public CustomAnimalTypeIdResolver() {
// 初始化类型和标识的映射
typeMap.put("dog_type", Dog.class);
typeMap.put("cat_type", Cat.class);
idMap.put(Dog.class, "dog_type");
idMap.put(Cat.class, "cat_type");
}
@Override
public void init(JavaType baseType) {
this.baseType = baseType;
}
@Override
public String idFromValue(Object value) {
return idMap.get(value.getClass());
}
@Override
public String idFromValueAndType(Object value, Class<?> suggestedType) {
return idFromValue(value);
}
@Override
public JavaType typeFromId(DatabindContext context, String id) throws IOException {
Class<?> clazz = typeMap.get(id);
if (clazz == null) {
throw new IllegalArgumentException("未知的类型标识: " + id);
}
return TypeFactory.defaultInstance().constructType(clazz);
}
@Override
public JsonTypeInfo.Id getMechanism() {
return JsonTypeInfo.Id.CUSTOM;
}
}
然后在父类注解中指定自定义解析器:
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonTypeIdResolver(CustomAnimalTypeIdResolver.class)
class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
此时JSON中的type属性值为dog_type时就会对应Dog类,cat_type对应Cat类,适配更复杂的类型识别场景。
常见注意事项
- 类型标识属性不要和父类的已有属性重名,否则会导致属性值被覆盖
- 如果子类还有进一步的继承关系,需要在子类上同样添加多态类型注解,才能实现多层级的类型识别
- 使用
ObjectMapper的activateDefaultTyping方法可以全局开启多态类型识别,但会存在一定的安全风险,不建议在生产环境直接使用 - 反序列化后如果需要操作子类特有方法,需要先进行类型判断再强转,避免ClassCastException
Jackson多态类型反序列化Java父类子类JsonTypeInfo修改时间:2026-07-08 16:15:34