若必填字段含空值,如何终止Jackson对整个对象的序列化?
解决方案:Jackson序列化时校验必填字段并终止序列化
问题背景
现有如下结构的JSON:
{ "field1": "value1", "field2": "value2", ... "field100": "value100" }
对应的POJO定义:
@Builder @Data public class BigJsonDto{ private String field1; private String field2; // ... 省略中间字段 private String field100; }
需求细节:
field1不可为空(值不能是null/空字符串)- 其余所有字段为必填但可为空(字段必须参与序列化,但允许值为null)
- 序列化时,只要有一个必填字段(含
field1)不符合要求(field1为空,或其他必填字段值为null),就终止整个对象的序列化,而非忽略空值输出JSON
实现方案:自定义Jackson序列化器
由于@JsonProperty(required = true)仅作用于反序列化,无法满足序列化时的校验需求,我们可以通过自定义序列化器实现前置校验逻辑:
步骤1:定义序列化必填字段的标记注解
先创建一个自定义注解,用来标记哪些字段是序列化时必须非空的(除了field1):
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SerializeRequired { }
步骤2:修改POJO并关联序列化器
更新POJO,用自定义注解标记必填字段,并指定自定义序列化器:
@Builder @Data @JsonSerialize(using = BigJsonDtoSerializer.class) public class BigJsonDto{ // field1不可为空 private String field1; @SerializeRequired private String field2; // ... 省略中间字段 @SerializeRequired private String field100; }
步骤3:编写自定义序列化器
实现StdSerializer,在序列化前添加校验逻辑,一旦校验失败就抛出异常终止序列化:
public class BigJsonDtoSerializer extends StdSerializer<BigJsonDto> { public BigJsonDtoSerializer() { super(BigJsonDto.class); } @Override public void serialize(BigJsonDto value, JsonGenerator gen, SerializerProvider provider) throws IOException { // 校验field1非空 if (value.getField1() == null || value.getField1().trim().isEmpty()) { throw new JsonProcessingException("field1 不能为空或空白字符串") {}; } // 校验所有标记@SerializeRequired的字段是否为null Field[] fields = BigJsonDto.class.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(SerializeRequired.class)) { field.setAccessible(true); try { Object fieldValue = field.get(value); if (fieldValue == null) { throw new JsonProcessingException(String.format("必填字段 %s 不能为null", field.getName())) {}; } } catch (IllegalAccessException e) { throw new JsonProcessingException("序列化校验时访问字段失败", e) {}; } } } // 校验通过,执行正常序列化 provider.defaultSerializeValue(value, gen); } }
步骤4:使用ObjectMapper进行序列化
在业务代码中使用ObjectMapper,捕获序列化异常即可处理校验失败的情况:
ObjectMapper mapper = new ObjectMapper(); BigJsonDto dto = BigJsonDto.builder() .field1("validValue") .field2(null) // 故意设置为null触发校验失败 .field100("value100") .build(); try { String json = mapper.writeValueAsString(dto); System.out.println("序列化结果:" + json); } catch (JsonProcessingException e) { // 校验失败,终止序列化,此处可添加异常处理逻辑 System.err.println("序列化终止:" + e.getMessage()); }
替代方案:序列化前手动校验
如果不想自定义序列化器,也可以结合Bean Validation在序列化前手动校验:
- 给POJO添加校验注解:
@Builder @Data public class BigJsonDto{ @NotNull(message = "field1不能为空") @NotBlank(message = "field1不能为空白字符串") private String field1; @NotNull(message = "field2不能为null") private String field2; // ... 省略中间字段 @NotNull(message = "field100不能为null") private String field100; }
- 序列化前执行校验:
Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); BigJsonDto dto = BigJsonDto.builder().field1("value1").field2(null).build(); Set<ConstraintViolation<BigJsonDto>> violations = validator.validate(dto); if (!violations.isEmpty()) { // 校验失败,不执行序列化 violations.forEach(v -> System.err.println(v.getMessage())); } else { ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(dto); System.out.println(json); }
内容的提问来源于stack exchange,提问作者Alexey Lvov
相关产品推荐
相关产品推荐

