Spring REST如何自动为返回的JSON对象添加@type类型字段
Spring REST接口自动给JSON序列化结果添加@type字段实现方案
Spring 5.3.x 默认使用Jackson作为JSON序列化组件,不需要自行开发定制序列化逻辑,使用Jackson原生能力即可实现需求,两种常用方案如下:
方案1:指定POJO添加注解(推荐按需使用场景)
直接在需要添加@type字段的POJO类上添加Jackson原生的@JsonTypeInfo注解即可,不需要修改原有业务方法、不需要添加getter,零侵入原有代码:
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; // 类上添加该注解,序列化时自动在根节点插入@type字段 @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@type" ) // 指定@type的字段值,如果不加该注解,默认会取类的简单名称 @JsonTypeName("DatasetInfo") public class DatasetInfo { // 原有字段、getter/setter、业务逻辑完全不需要改动 private String title; private String organization; private List<SpecieInfo> species; // ... 其余原有代码 }
SpecieInfo类也添加相同规则的注解,序列化后会自动在每个对象根节点生成对应@type字段,嵌套对象也会正常生效。
方案2:全局配置自动添加(推荐全项目统一生效场景)
如果需要项目中所有序列化返回的POJO自动添加@type字段,不需要逐个类加注解,直接自定义Jackson的ObjectMapper配置即可:
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.DatabindContext; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import java.io.IOException; @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { ObjectMapper objectMapper = builder.createXmlMapper(false).build(); // 自定义类型解析器,返回类的简单名称,兼容匿名子类场景 TypeIdResolver simpleNameIdResolver = new TypeIdResolver() { @Override public String idFromValue(Object value) { return getClassName(value.getClass()); } @Override public String idFromValueAndType(Object value, Class<?> suggestedType) { return getClassName(suggestedType); } @Override public String idFromBaseType() { return null; } @Override public JavaType typeFromId(DatabindContext context, String id) throws IOException { // 仅做序列化输出时不需要实现反序列化逻辑,留空即可 return null; } @Override public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.NAME; } // 处理匿名内部类场景,取父类类名 private String getClassName(Class<?> clazz) { if (clazz.isAnonymousClass()) { return clazz.getSuperclass().getSimpleName(); } return clazz.getSimpleName(); } }; // 全局配置:所有非简单类型对象序列化时自动添加@type属性 StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder( ObjectMapper.DefaultTyping.NON_FINAL, objectMapper.getPolymorphicTypeValidator() ) { @Override public boolean useForType(JavaType t) { // 排除基础类型、字符串、集合等不需要加@type的类型,只处理自定义POJO return !t.isPrimitive() && !t.isCollectionLikeType() && !t.isMapLikeType() && t.getRawClass() != String.class && super.useForType(t); } }; typeResolverBuilder.init(JsonTypeInfo.Id.NAME, simpleNameIdResolver) .inclusion(JsonTypeInfo.As.PROPERTY) .typeProperty("@type"); objectMapper.setDefaultTyping(typeResolverBuilder); return objectMapper; } }
该配置会自动给所有自定义POJO(包括示例中返回的匿名内部类实例)添加@type字段,字段值为对应类的简单类名,基础类型、字符串、集合、Map等类型不会添加冗余字段。
方案对比
- 注解方案:灵活度高,按需添加,不需要额外配置,适合只有部分接口/POJO需要添加
@type字段的场景。 - 全局配置方案:一次配置全项目生效,不需要改动原有POJO代码,适合所有返回对象统一需要添加类型标识的场景。
- 两种方案都是Jackson原生支持的能力,不需要引入额外依赖,也不需要编写重复的getType()方法,性能和稳定性远高于自定义父类方法的实现。
内容的提问来源于stack exchange,提问作者zakmck
相关产品推荐
相关产品推荐

