如何阻止Jackson为已知类型列表元素序列化@class属性?
如何在Jackson序列化已知类型列表时省略@class属性(不影响全局多态配置)
在Java应用中使用Jackson序列化包含已知类型列表的对象时,Jackson会为列表元素自动添加@class属性,但这些属性在类型明确的场景下属于冗余内容。例如以下JSON输出中,items列表的元素类型Item是已知的,需要移除对应的@class字段:
[ { "@class": "com.example.model.Resource", "id": "Resource_1", "version": 0, "reference": false, "items": [ { "@class": "com.example.model.Item", // 需要移除 "id": "Item01", "version": 0, "status": "ACTIVE", "index": 1, "url": "https://example.com/item/1" }, { "@class": "com.example.model.Item", // 需要移除 "id": "Item02", "version": 0, "status": "ACTIVE", "index": 2, "url": "https://example.com/item/2" } ] } ]
由于应用其他区域依赖多态类型处理,无法全局禁用Jackson的类型信息输出,仅需在类型明确的特定列表中抑制@class属性。
方法一:在目标字段上使用@JsonTypeInfo局部禁用类型信息
最直接的方式是在包含已知类型列表的字段上添加@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)注解,覆盖全局的多态类型配置,让Jackson不再为该列表的元素输出@class属性。
示例代码(Resource类):
import com.fasterxml.jackson.annotation.JsonTypeInfo; import java.util.List; public class Resource { private String id; private int version; private boolean reference; // 为该字段禁用类型信息输出 @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) private List<Item> items; // getter、setter 省略 } class Item { private String id; private int version; private String status; private int index; private String url; // getter、setter 省略 }
这种方式仅作用于标注的items字段,不会影响应用中其他需要多态类型处理的场景。
方法二:编程式指定序列化类型(无需修改实体类)
如果无法修改实体类,可以通过ObjectMapper的writerFor方法,明确指定序列化时使用的具体类型,强制Jackson识别列表元素的已知类型,从而省略@class属性。
示例代码:
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; public class SerializationExample { public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); // 全局保留多态类型配置(无需修改) List<Resource> resourceList = // 初始化你的数据列表 // 明确指定序列化的目标类型,让Jackson知晓items的元素类型 String json = mapper.writerFor(new TypeReference<List<Resource>>() {}) .writeValueAsString(resourceList); System.out.println(json); } }
这种方式适合无法修改实体类的场景,仅在特定序列化操作中生效,不影响全局配置。
方法三:自定义模块覆盖类型解析策略
如果需要对多个类似字段统一处理,可以自定义Jackson模块,针对特定类型的列表字段禁用类型信息输出。
示例代码:
import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; import com.fasterxml.jackson.databind.ser.Serializers; import java.util.List; public class NoTypeInfoListModule extends SimpleModule { @Override public void setupModule(SetupContext context) { super.setupModule(context); context.addBeanSerializerModifier(new BeanSerializerModifier() { @Override public Serializers modifySerializers(Config config, Serializers serializers) { return new Serializers.Base() { // 针对List<Item>类型的序列化,禁用类型信息 @Override public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) { if (type.isCollectionLikeType() && type.getContentType().getRawClass() == Item.class) { // 使用默认序列化器,但禁用类型信息 JsonSerializer<?> serializer = super.findSerializer(config, type, beanDesc); if (serializer instanceof ContainerSerializer<?>) { return ((ContainerSerializer<?>) serializer) .withValueTypeSerializer(null); } } return super.findSerializer(config, type, beanDesc); } }; } }); } }
然后将模块注册到ObjectMapper:
mapper.registerModule(new NoTypeInfoListModule());
这种方式适合需要批量处理同类型列表的场景,无需逐个修改实体类字段。
内容的提问来源于stack exchange,提问作者Krzysztof Grabowski
相关产品推荐
相关产品推荐

