Jackson序列化List多态元素时丢失@JsonTypeInfo包装层问题
问题根因
包装丢失的核心原因是多态序列化注解只加在了具体子类上,没有在BaseWidget基类做声明。
Jackson处理多态类型的逻辑是:只有当值的声明静态类型是配置了多态规则的类型时,才会触发类型包装逻辑:
- 直接序列化
TextParagraph实例时,静态类型就是子类本身,类上的@JsonTypeInfo能被正常识别,所以能输出带textParagraph外层包装的正确结构 - 把实例存入
List<BaseWidget>时,集合元素的静态声明类型是BaseWidget,如果基类上没有配置多态规则,Jackson会把元素当做普通POJO处理,直接平铺序列化所有字段,外层的类型key自然就丢了。
解决方案
不需要自定义ObjectMapper配置,也不需要额外写包装类,只需要把多态配置统一放到基类上,声明所有子类型的映射即可:
- 修改
BaseWidget类,添加多态配置注解:
@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) @JsonSubTypes({ @JsonSubTypes.Type(value = TextParagraph.class, name = "textParagraph"), @JsonSubTypes.Type(value = Image.class, name = "image"), @JsonSubTypes.Type(value = ButtonList.class, name = "buttonList") // 后续新增其他Widget类型,在这里追加映射即可 }) public abstract class BaseWidget { // 基类公共字段、方法 }
- 子类上原有的
@JsonTypeInfo可以删除,@JsonTypeName可留可删——基类已经统一声明了包装对象的序列化规则,子类重复配置不会报错,但统一在基类维护更不容易出现规则不一致的问题。
效果验证
修改完成后再序列化List<BaseWidget>类型的字段,Jackson会自动识别每个元素的实际子类型,输出完全符合Google Card v1规范的结构:
{ "widgets": [ { "textParagraph": { "text": "Testing Text" } }, { "image": { "imageUrl": "https://cataas.com/cat", "altText": "Random cat" } }, { "buttonList": { "buttons": [ {"text": "Happy"}, {"text": "Sad"} ] } } ] }
如果
BaseWidget是接口而非抽象类,直接把相同的注解加在接口声明上即可,逻辑完全一致。
内容的提问来源于stack exchange,提问作者Evan Ruff
相关产品推荐
相关产品推荐

