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

如何通过@JsonSerialize在运行时创建包装器并保留默认字段序列化?

解决Jackson动态根节点包装(无需每个子类加@JsonRootName)

嗨,这个需求我太熟了——想要给所有Request子类自动套一个以类名为名称的根节点,又不想在每个子类上重复加注解,同时还得复用Jackson的默认序列化逻辑对吧?

你之前遇到的无限递归问题,根源是直接调用writeObject或者defaultSerializeValue时,Jackson又会触发你这个自定义的CustomSerializer,陷入循环。解决的核心是获取对应子类的默认序列化器,绕过我们自己的这个。

直接上修改后的代码:

@JsonSerialize(using = CustomSerializer.class)
public abstract class Request {
    public static class CustomSerializer extends JsonSerializer<Request> {
        @Override
        public void serialize(Request request, JsonGenerator jgen, SerializerProvider provider) throws IOException {
            // 获取当前子类的默认序列化器,避开我们自定义的这个
            JsonSerializer<Object> defaultSerializer = provider.findValueSerializer(request.getClass(), null);
            
            // 写入动态根节点:用子类的类名作为包装器名称
            jgen.writeStartObject();
            jgen.writeFieldName(request.getClass().getSimpleName());
            
            // 用默认序列化器处理字段,完全复用Jackson原生逻辑,不会递归
            defaultSerializer.serialize(request, jgen, provider);
            
            // 关闭根节点对象
            jgen.writeEndObject();
        }
    }
}

关键逻辑解释:

  • provider.findValueSerializer(request.getClass(), null):这里会查找具体子类的默认序列化器(比如你的UserRequest、OrderRequest这些子类的BeanSerializer),而不是我们的CustomSerializer,所以不会触发递归。
  • 整个流程就是:先写外层的根节点对象,把子类类名作为字段名,然后用默认序列化器把子类的所有字段序列化到这个字段下面,最后关闭外层对象。
  • 子类原来的所有Jackson注解(比如@JsonProperty、@JsonIgnore、@JsonFormat)都会生效,因为我们完全复用了默认的序列化逻辑,只是加了一层包装。

举个例子,如果你的子类是:

public class UserRequest extends Request {
    private String username;
    private Integer age;
    // getters & setters
}

序列化后会得到:

{
  "UserRequest": {
    "username": "Alice",
    "age": 25
  }
}

完美符合你的需求——不用给每个子类加@JsonRootName,包装器名称自动用类名,字段序列化完全交给Jackson处理~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:14:04