如何移除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
相关产品推荐
相关产品推荐

