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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 08:33:20