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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 05:24:29