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

如何用Jackson实现多子类型接口序列化以匹配指定JSON结构

问题描述

目标JSON格式(同一时间仅包含actionA/actionB/actionC中的一个):

{
  "name": "foo",
  "actionA": {
    ....
  }
}

当前使用Jackson序列化后得到的JSON多了一层action嵌套,不符合需求:

{
  "name": "foo",
  "action": {
    "actionA": {
      ...
    }
  }
}

现有Java类结构:

public record Content(String name, Action action) {
...
}
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({
  @JsonSubTypes.Type(value = ActionA.class),
  @JsonSubTypes.Type(value = ActionB.class),
  @JsonSubTypes.Type(value = ActionC.class)
})
public interface Action {
...
}
@JsonTypeName("actionA")
public record ActionA(...) {
...
}
@JsonTypeName("actionB")
public record ActionB(...) {
...
}
@JsonTypeName("actionC")
public record ActionC(...) {
...
}
解决方案

可以通过@JsonAnyGetter和@JsonAnySetter实现将action的内容直接展开到Content的顶层属性中,具体修改如下:

  1. 修改Content记录,添加动态属性的获取和设置方法:
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;

public record Content(String name, Action action) {
    // 复用ObjectMapper避免重复创建
    private static final ObjectMapper MAPPER = new ObjectMapper();

    @JsonAnyGetter
    public Map<String, Object> getActionProperties() {
        // 将Action实例转换为包含类型名作为键的Map
        return MAPPER.convertValue(action, new TypeReference<Map<String, Object>>() {});
    }

    @JsonAnySetter
    public void setActionProperty(String key, Object value) throws Exception {
        // 根据键名匹配对应的Action子类
        switch (key) {
            case "actionA" -> action = MAPPER.convertValue(value, ActionA.class);
            case "actionB" -> action = MAPPER.convertValue(value, ActionB.class);
            case "actionC" -> action = MAPPER.convertValue(value, ActionC.class);
            default -> throw new IllegalArgumentException("Unknown action type: " + key);
        }
    }
}
  1. 保留原有Action接口和子类的注解不变,MAPPER.convertValue会利用@JsonTypeName和@JsonTypeInfo将Action实例序列化为以类型名为键的对象,再通过@JsonAnyGetter将这个键值对展开到Content的顶层。

修改完成后,序列化Content实例时,actionA/actionB/actionC会直接和name同级,完全匹配目标JSON格式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 07:23:15