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

如何移除Jackson自定义序列化器生成的Null属性?

解决Jackson自定义序列化器忽略@JsonInclude的问题

我之前也碰到过类似的Jackson自定义序列化器和@JsonInclude注解不兼容的情况,给你几个可行的解决方案:

方案一:自定义BeanSerializerModifier修改字段序列化逻辑

这个方案直接从根源入手,修改Jackson处理字段序列化的逻辑,在写入字段名前就判断是否需要跳过null值,完全尊重@JsonInclude的配置规则。

步骤1:实现自定义BeanSerializerModifier

创建一个BeanSerializerModifier的子类,重写changeProperties方法,替换目标字段的BeanPropertyWriter:

public class NullSkippingBeanSerializerModifier extends BeanSerializerModifier {
    @Override
    public List<BeanPropertyWriter> changeProperties(
            SerializationConfig config,
            BeanDescription beanDesc,
            List<BeanPropertyWriter> beanProperties) {

        for (int i = 0; i < beanProperties.size(); i++) {
            BeanPropertyWriter writer = beanProperties.get(i);
            // 匹配目标字段名
            if ("activeContract".equals(writer.getName())) {
                beanProperties.set(i, new NullSkippingPropertyWriter(writer));
            }
        }
        return beanProperties;
    }

    // 包装原BeanPropertyWriter,重写序列化逻辑
    private static class NullSkippingPropertyWriter extends BeanPropertyWriter {
        private NullSkippingPropertyWriter(BeanPropertyWriter base) {
            super(base);
        }

        @Override
        public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
            // 获取字段的实际值(包括自定义序列化器计算后的值)
            Object value = get(bean);
            
            // 获取当前的包含策略(优先属性级别的@JsonInclude,其次是类级别或全局配置)
            JsonInclude.Value include = getAnnotation(JsonInclude.class);
            JsonInclude.Include inclusion = include == null 
                    ? prov.getDefaultPropertyInclusion().getValueInclusion() 
                    : include.value();

            // 如果值为null且符合跳过规则,直接返回不序列化
            if (value == null && (inclusion == JsonInclude.Include.NON_NULL || inclusion == JsonInclude.Include.NON_EMPTY)) {
                return;
            }

            // 否则执行原有序列化逻辑
            super.serializeAsField(bean, gen, prov);
        }
    }
}

步骤2:注册到ObjectMapper

把自定义的Modifier注册到Jackson的ObjectMapper中,让全局生效:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new SimpleModule() {
    @Override
    public void setupModule(SetupContext context) {
        super.setupModule(context);
        context.addBeanSerializerModifier(new NullSkippingBeanSerializerModifier());
    }
});

这个方案的优点是不破坏原有自定义序列化器的逻辑,同时完美适配@JsonInclude的所有配置规则。

方案二:修改自定义序列化器,主动跳过null值

如果不想全局修改,也可以在JacksonSpringSpelSerializer中添加逻辑,当计算后的值为null时,通过抛出特定异常让Jackson跳过整个字段:

public class JacksonSpringSpelSerializer extends JsonSerializer<Object> {
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        // 执行原有SpEL计算逻辑,得到实际值
        Contract calculatedValue = // 你的SpEL计算代码
        
        if (calculatedValue == null) {
            // 抛出异常让Jackson捕获并跳过该字段
            throw new JsonIgnoreException("Skipping null activeContract");
        }
        
        // 正常序列化计算后的值
        gen.writeObject(calculatedValue);
    }
}

这个方案胜在简单,但依赖Jackson对异常的处理逻辑,不如方案一稳妥,而且需要修改自定义序列化器的原有代码。

方案三:尝试属性级别的@JsonInclude注解

虽然你提到类级别的@JsonInclude被忽略,但可以尝试在activeContract属性上直接添加属性级别的注解,强制指定包含规则:

@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonSerialize(nullsUsing = JacksonSpringSpelSerializer.class, using = JacksonSpringSpelSerializer.class)
@JsonView(View.Contract.class)
@Value("#{@contractService.getActiveContract(#this)}")
public Contract activeContract;

不过这个方案不一定生效,因为自定义序列化器通常会绕过Jackson的默认包含检查,优先级不如前两个方案高。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 19:59:07