如何配置Jackson ObjectMapper以排除所有嵌套布尔字段均为null的空对象
这个问题我之前也碰到过,根源在于你用的@JsonInclude(NON_NULL)只盯着字段本身是不是null——你的holdTypeIndicator是个已经实例化的对象,哪怕它内部所有字段都是null,Jackson也会把这个空对象序列化出来,所以才会出现{"holdTypeIndicator": {}}这种情况。NON_DEFAULT和NON_ABSENT也解决不了,因为对象本身不是默认的null值。
下面给你一个全局生效的解决方案,不用挨个给嵌套字段加注解,通过自定义Jackson的序列化器修饰器就能实现“嵌套对象全字段为null时自动排除”的效果:
步骤1:自定义序列化器修饰器
这个修饰器会拦截所有对象的序列化过程,检查对象的所有字段是否都为null,如果是,就把它当作null处理,这样NON_NULL策略就会自动排除这个字段:
import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; import java.lang.reflect.Field; public class NullObjectSerializerModifier extends BeanSerializerModifier { @Override public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) { return new NullObjectWrapperSerializer((JsonSerializer<Object>) serializer); } private static class NullObjectWrapperSerializer extends StdSerializer<Object> { private final JsonSerializer<Object> delegate; public NullObjectWrapperSerializer(JsonSerializer<Object> delegate) { super(Object.class); this.delegate = delegate; } @Override public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException { boolean allFieldsNull = true; // 反射遍历对象所有字段,检查是否全为null Field[] fields = value.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); try { if (field.get(value) != null) { allFieldsNull = false; break; } } catch (IllegalAccessException e) { // 遇到访问异常时默认视为非空,保证序列化正常进行 allFieldsNull = false; break; } } if (allFieldsNull) { // 全字段为null时输出null,让NON_NULL策略排除该字段 gen.writeNull(); } else { // 正常序列化对象 delegate.serialize(value, gen, provider); } } } }
步骤2:配置ObjectMapper
把上面的修饰器注册到ObjectMapper中,同时开启全局的NON_NULL排除策略:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.annotation.JsonInclude; public class CustomObjectMapperFactory { public static ObjectMapper create() { ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.setSerializerModifier(new NullObjectSerializerModifier()); mapper.registerModule(module); // 全局排除值为null的字段 mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); return mapper; } }
步骤3:使用自定义ObjectMapper序列化
现在用这个配置好的ObjectMapper来序列化你的对象:
HoldsType holds = new HoldsType(); holds.setHoldTypeIndicator(new HoldTypeIndicatorType()); // 内部所有字段都是null ObjectMapper mapper = CustomObjectMapperFactory.create(); String json = mapper.writeValueAsString(holds); // 输出结果:{} —— holdTypeIndicator被自动排除了
补充说明
- 这个方案是全局生效的,所有自定义类都会被检查,只要对象全字段为null就会被排除,不用修改现有类的注解。
- 如果只想针对特定类生效,可以在
modifySerializer方法里加个判断,比如if (beanDesc.getBeanClass().equals(HoldTypeIndicatorType.class)),再包装序列化器。 - 如果你类数量不多,也可以用替代方案:在嵌套字段上用
@JsonInclude(value = JsonInclude.Include.NON_EMPTY, content = JsonInclude.Include.NON_NULL),然后让嵌套类实现isEmpty()方法,判断自己的字段是否全为null。但这个方案需要每个嵌套类都写isEmpty(),适合小体量场景。
内容的提问来源于stack exchange,提问作者Janusz Januszewski




