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

Jackson:仅为主/容器类型开启默认类型的配置方法咨询

仅为指定容器类开启Jackson默认类型的解决方案

要实现仅对标记的「容器类」(如你的Document类)自动添加类型信息(@class字段),同时不对其内部类、集合或包装类型启用默认类型,且无需手动给每个容器类添加@JsonTypeInfo注解,可通过以下步骤实现:

1. 自定义容器类标记注解

先定义一个注解,用来标记哪些类是需要开启默认类型的容器类:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContainerType {
}

2. 自定义TypeResolverBuilder实现规则

继承Jackson的StdTypeResolverBuilder,重写useForType方法,仅对标记了@ContainerType的类启用类型信息:

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;

public class ContainerOnlyTypeResolverBuilder extends StdTypeResolverBuilder {
    @Override
    public boolean useForType(JavaType type) {
        // 仅对标记了@ContainerType的类启用类型信息
        return type.getRawClass().isAnnotationPresent(ContainerType.class);
    }
}

3. 配置ObjectMapper使用自定义Builder

在配置ObjectMapper时,指定使用我们自定义的类型解析器,并设置类型信息的存储方式(如作为属性@class):

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;

public class ObjectMapperConfig {
    public static ObjectMapper createCustomObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        
        // 初始化自定义类型解析器
        TypeResolverBuilder<?> typeResolverBuilder = new ContainerOnlyTypeResolverBuilder()
                .init(com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS, null)
                .inclusion(com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY)
                .typeProperty("@class"); // 指定类型字段名
        
        // 为ObjectMapper设置自定义默认类型规则
        objectMapper.setDefaultTyping(typeResolverBuilder);
        
        return objectMapper;
    }
}

4. 标记容器类

给需要开启默认类型的容器类添加@ContainerType注解,比如你的Document类:

@ContainerType
public class Document {
    private String id;
    private Long testingLong;
    private Long accountId;
    private Internal internal;
    private List<String> list;
    
    // 省略getter、setter、构造方法
}

// 内部类无需标记,不会生成类型信息
public class Internal {
    private String id;
    
    // 省略getter、setter、构造方法
}

5. 测试效果

序列化Document实例时,只有Document会生成@class字段,内部的Internal、Long、List都不会添加额外类型信息:

public class TestSerialization {
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = ObjectMapperConfig.createCustomObjectMapper();
        
        Internal internal = new Internal();
        internal.setId("1");
        
        Document document = new Document();
        document.setId(null);
        document.setTestingLong(5L);
        document.setAccountId(10L);
        document.setInternal(internal);
        document.setList(List.of("1", "2", "3"));
        
        String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(document);
        System.out.println(json);
    }
}

输出的JSON如下,符合需求:

{
  "@class" : "com.example.demo.test.Document",
  "id" : null,
  "testingLong" : 5,
  "accountId" : 10,
  "internal" : {
    "id" : "1"
  },
  "list" : [ "1", "2", "3" ]
}

扩展说明

  • 如果需要批量标记容器类(比如某个包下的所有类),可以修改useForType方法的判断逻辑,比如检查类的包名是否符合规则,无需逐个添加注解。
  • 若反序列化时根对象类型为Object,Jackson会通过@class字段自动解析为对应的容器类实例,内部字段则按类定义的类型正常反序列化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 01:17:41