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

Jackson中类含自定义Serializer时,SerializerModifier不处理嵌套对象

问题原因

Jackson序列化器的优先级规则是:类上@JsonSerialize(using=...)指定的序列化器 > 通过BeanSerializerModifier修改的序列化器。当你给Foo类添加@JsonSerialize(using = FooSerializer.class)后,Jackson会直接使用FooSerializer,跳过你的DepthLimitedSerializer替换逻辑——你的Modifier只处理了BeanSerializerBase类型的序列化器,而FooSerializer不属于这个类型,所以modifySerializer方法不会对它生效,深度限制逻辑自然被忽略。

可行解决方案

方案1:在自定义序列化器中集成深度控制逻辑

如果FooSerializer是你自己维护的,最简单的方式是把深度限制的逻辑抽成工具类,然后在FooSerializer中复用这个逻辑,和DepthLimitedSerializer保持一致的判断逻辑。

步骤1:抽离深度控制工具类

public class DepthControl {
    public static final ThreadLocal<Integer> maxDepth = ThreadLocal.withInitial(() -> 2);
    public static final ThreadLocal<Integer> currentDepth = ThreadLocal.withInitial(() -> -1);

    // 判断是否需要完整序列化
    public static boolean shouldSerializeFull() {
        return maxDepth.get() < 0 || currentDepth.get() < maxDepth.get();
    }

    // 进入嵌套层级
    public static void enterDepth() {
        currentDepth.set(currentDepth.get() + 1);
    }

    // 退出嵌套层级
    public static void exitDepth() {
        currentDepth.set(currentDepth.get() - 1);
    }
}

步骤2:修改DepthLimitedSerializer使用工具类

public class DepthLimitedSerializer extends BeanSerializer {
    public DepthLimitedSerializer(BeanSerializerBase src, int depth) {
        super(src);
        DepthControl.maxDepth.set(depth);
    }

    @Override
    protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (DepthControl.shouldSerializeFull()) {
            DepthControl.enterDepth();
            super.serializeFields(bean, gen, provider);
            DepthControl.exitDepth();
        } else {
            try {
                Arrays.stream(_props)
                    .filter(p -> p.getName().equals("id"))
                    .findFirst().orElseThrow()
                    .serializeAsField(bean, gen, provider);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

步骤3:修改FooSerializer加入深度控制

public class FooSerializer extends StdSerializer<Foo> {
    public FooSerializer() {
        super(Foo.class);
    }

    @Override
    public void serialize(Foo foo, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (DepthControl.shouldSerializeFull()) {
            DepthControl.enterDepth();
            gen.writeStartObject();
            // 序列化父类字段
            gen.writeStringField("id", foo.getId());
            gen.writeStringField("someBaseProperty", foo.getSomeBaseProperty());
            // 序列化当前类字段
            gen.writeStringField("someFooProperty", foo.getSomeFooProperty());
            gen.writeEndObject();
            DepthControl.exitDepth();
        } else {
            // 仅序列化id
            gen.writeStartObject();
            gen.writeStringField("id", foo.getId());
            gen.writeEndObject();
        }
    }
}

方案2:用包装器包装自定义序列化器(无需修改原有FooSerializer)

如果不想修改已有的FooSerializer,可以写一个通用的深度限制包装器,在BeanSerializerModifier中把FooSerializer替换为包装后的实例。

步骤1:实现通用深度包装序列化器

import java.lang.reflect.Method;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

public class DepthWrappingSerializer<T> extends JsonSerializer<T> {
    private final JsonSerializer<T> delegate;

    public DepthWrappingSerializer(JsonSerializer<T> delegate) {
        this.delegate = delegate;
    }

    @Override
    public void serialize(T value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (DepthControl.shouldSerializeFull()) {
            DepthControl.enterDepth();
            delegate.serialize(value, gen, provider);
            DepthControl.exitDepth();
        } else {
            // 仅序列化id字段(通用实现,可根据实际调整)
            gen.writeStartObject();
            try {
                Method getIdMethod = value.getClass().getMethod("getId");
                String id = (String) getIdMethod.invoke(value);
                gen.writeStringField("id", id);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException("Failed to serialize id field", e);
            }
            gen.writeEndObject();
        }
    }
}

步骤2:修改BeanSerializerModifier处理FooSerializer

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanSerializerBase;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;

public class DepthLimitModifier extends BeanSerializerModifier {
    private final int maxDepth;

    public DepthLimitModifier(int maxDepth) {
        this.maxDepth = maxDepth;
    }

    @Override
    public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
        // 处理默认BeanSerializer
        if (serializer instanceof BeanSerializerBase) {
            return new DepthLimitedSerializer((BeanSerializerBase) serializer, maxDepth);
        }
        // 处理自定义的FooSerializer
        if (serializer instanceof FooSerializer) {
            return new DepthWrappingSerializer<>(serializer);
        }
        return serializer;
    }
}

方案3:让自定义序列化器实现ContextualSerializer感知深度

通过实现ContextualSerializer接口,让FooSerializer在序列化时感知当前的深度上下文,动态调整序列化行为。

实现上下文感知的FooSerializer

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

public class FooSerializer extends StdSerializer<Foo> implements ContextualSerializer {
    private Integer currentDepth;
    private Integer maxDepth;

    // 默认构造器
    public FooSerializer() {
        super(Foo.class);
    }

    // 带深度参数的构造器(内部使用)
    private FooSerializer(Integer currentDepth, Integer maxDepth) {
        super(Foo.class);
        this.currentDepth = currentDepth;
        this.maxDepth = maxDepth;
    }

    @Override
    public void serialize(Foo foo, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (maxDepth < 0 || currentDepth < maxDepth) {
            // 完整序列化
            gen.writeStartObject();
            gen.writeStringField("id", foo.getId());
            gen.writeStringField("someBaseProperty", foo.getSomeBaseProperty());
            gen.writeStringField("someFooProperty", foo.getSomeFooProperty());
            gen.writeEndObject();
        } else {
            // 仅序列化id
            gen.writeStartObject();
            gen.writeStringField("id", foo.getId());
            gen.writeEndObject();
        }
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        // 从ThreadLocal获取当前深度和最大深度
        Integer current = DepthControl.currentDepth.get();
        Integer max = DepthControl.maxDepth.get();
        return new FooSerializer(current, max);
    }
}
注意事项
  • 使用ThreadLocal时,务必在序列化完成后清理,避免内存泄漏:
    try {
        DepthControl.maxDepth.set(1);
        String json = objectMapper.writeValueAsString(bar);
        // 处理json
    } finally {
        DepthControl.maxDepth.remove();
        DepthControl.currentDepth.remove();
    }
    
  • 如果你的项目中有多个自定义序列化器,方案2的通用包装器可以扩展支持更多类型,无需逐个修改序列化器代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 12:02:13