
在使用Jackson进行JSON序列化时,我们经常遇到需要处理字段空值的情况。Jackson默认的行为是:
然而,这些方法并不能满足所有场景的需求。有时,我们希望某个POJO对象中的某些字段是“必填”的,如果在序列化时发现这些必填字段为null,我们不希望仅仅是忽略它们或将它们序列化为null,而是希望整个对象的序列化过程都失败,以此来强制数据完整性或提前发现数据问题。
尽管Jackson提供了@JsonProperty(required = true)注解,但此注解主要用于反序列化(deserialization)时校验JSON中是否存在对应的字段,如果缺失则抛出异常。它并不能在序列化(serialization)阶段阻止带有null必填字段的对象被序列化。
为了解决这一问题,我们需要一种机制,能够在对象序列化之前介入,执行自定义的校验逻辑。
Jackson提供了强大的扩展能力,允许我们通过自定义序列化器(Custom JsonSerializer)来完全控制对象的序列化过程。核心思想是创建一个继承自JsonSerializer<T>的类,并重写其serialize()方法。
假设我们有一个BigJsonDto类,其中包含多个字段,我们需要确保其中某些字段(例如field1、field2等)在序列化时不能为null。
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;
// 假设这是我们的数据传输对象
// @Builder 和 @Data 注解来自 Lombok,用于简化POJO创建
// @JsonSerialize 注解将在后续介绍
public class BigJsonDto {
private String field1;
private String field2;
private String field3;
// ... 其他字段
private String fieldN;
// 省略构造函数、getter/setter等Lombok生成的方法
// 为了演示,我们手动添加一个简单的构造函数
public BigJsonDto(String field1, String field2, String field3, String fieldN) {
this.field1 = field1;
this.field2 = field2;
this.field3 = field3;
this.fieldN = fieldN;
}
public String getField1() { return field1; }
public String getField2() { return field2; }
public String getField3() { return field3; }
public String getFieldN() { return fieldN; }
}
// 自定义BigJsonDto的序列化器
public class BigJsonDtoSerializer extends JsonSerializer<BigJsonDto> {
// 定义一个谓词,用于检查BigJsonDto实例是否有效(即必填字段是否为null)
public static final Predicate<BigJsonDto> IS_NOT_VALID =
dto -> Stream.of(dto.getField1(), // 添加所有必填字段
dto.getField2(),
dto.getField3(),
dto.getFieldN())
.anyMatch(Objects::isNull); // 只要有一个为null,则认为无效
@Override
public void serialize(BigJsonDto value, JsonGenerator gen,
SerializerProvider serializers) throws IOException {
// 1. 执行校验逻辑
if (IS_NOT_VALID.test(value)) {
// 如果对象无效(必填字段为null),则抛出JsonGenerationException,阻止序列化
throw new JsonGenerationException("BigJsonDto实例无效:存在必填字段为null。", gen);
}
// 2. 如果校验通过,则手动执行序列化
// 注意:这里需要手动将每个字段写入JsonGenerator
gen.writeStartObject(); // 开始写入JSON对象
// 写入每个字段
gen.writeFieldName("field1");
gen.writeString(value.getField1());
gen.writeFieldName("field2");
gen.writeString(value.getField2());
gen.writeFieldName("field3");
gen.writeString(value.getField3());
// ... 依此类推,写入所有需要序列化的字段
gen.writeFieldName("fieldN");
gen.writeString(value.getFieldN());
gen.writeEndObject(); // 结束写入JSON对象
}
/**
* 此方法仅在通过Jackson模块注册序列化器时需要重写。
* 它告诉Jackson此序列化器处理哪种类型的对象。
*/
@Override
public Class<BigJsonDto> handledType() {
return BigJsonDto.class;
}
}代码解析:
创建了自定义序列化器之后,我们需要将其注册到Jackson的ObjectMapper中,以便Jackson知道在序列化BigJsonDto时应该使用我们的自定义逻辑。主要有两种注册方式:
这是最直接的方式,适用于为特定POJO类指定序列化器。
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
@JsonSerialize(using = BigJsonDtoSerializer.class)
public class BigJsonDto {
private String field1;
private String field2;
private String field3;
private String fieldN;
// 省略构造函数、getter/setter等
public BigJsonDto(String field1, String field2, String field3, String fieldN) {
this.field1 = field1;
this.field2 = field2;
this.field3 = field3;
this.fieldN = fieldN;
}
public String getField1() { return field1; }
public String getField2() { return field2; }
public String getField3() { return field3; }
public String getFieldN() { return fieldN; }
}通过在BigJsonDto类上添加@JsonSerialize(using = BigJsonDtoSerializer.class)注解,Jackson在遇到BigJsonDto类型的对象时,会自动使用BigJsonDtoSerializer进行序列化。在这种方式下,BigJsonDtoSerializer中的handledType()方法不是必需的,但保留它也无害。
这种方式更灵活,特别是当你需要注册多个自定义序列化器,或者希望将序列化器配置集中管理时。它也常用于Spring Boot等框架中,通过将模块注册为Bean来自动配置ObjectMapper。
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.util.List;
import java.util.Map;
public class SerializationModuleConfig {
public static void registerCustomSerializers(ObjectMapper mapper) {
// 创建一个SimpleModule实例
SimpleModule mySerializationModule = new SimpleModule(
"MyCustomSerializationModule", // 模块名称
new Version(1, 0, 0, null, "com.example", "my-app"), // 模块版本信息
Map.of(), // 反序列化器列表 (此处不涉及,为空Map)
List.of(new BigJsonDtoSerializer()) // 序列化器列表,添加我们的自定义序列化器
);
// 将模块注册到ObjectMapper
mapper.registerModule(mySerializationModule);
}
// 如果在Spring Boot中使用,可以将其注册为一个Bean
/*
@Configuration
public class JacksonConfig {
@Bean
public Module customSerializationModule() {
SimpleModule module = new SimpleModule(
"MyCustomSerializationModule",
new Version(1, 0, 0, null, "com.example", "my-app")
);
module.addSerializer(BigJsonDto.class, new BigJsonDtoSerializer());
return module;
}
}
*/
}说明:
以下是一个完整的示例,演示了如何使用自定义序列化器来阻止空值必填字段的对象被序列化。
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize; // 如果使用注解方式
public class SerializationDemo {
// 假设BigJsonDto和BigJsonDtoSerializer如前文所示
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
// 方式一:如果BigJsonDto类上使用了@JsonSerialize注解
// ObjectMapper mapper = new ObjectMapper(); // 无需额外配置
// 方式二:如果使用模块注册方式
// SerializationModuleConfig.registerCustomSerializers(mapper);
// 示例1:必填字段为null,预期序列化失败
System.out.println("--- 示例1:必填字段为null ---");
BigJsonDto invalidDto = new BigJsonDto(null, "value2", "value3", "valueN");
try {
String resultingJson = mapper.writeValueAsString(invalidDto);
System.out.println("序列化成功 (不应该发生): " + resultingJson);
} catch (JsonGenerationException e) {
System.err.println("序列化失败,符合预期:" + e.getMessage());
} catch (Exception e) {
System.err.println("发生其他异常:" + e.getMessage());
}
System.out.println("\n--- 示例2:所有必填字段都有值 ---");
// 示例2:所有必填字段都有值,预期序列化成功
BigJsonDto validDto = new BigJsonDto("value1", "value2", "value3", "valueN");
try {
String resultingJson = mapper.writeValueAsString(validDto);
System.out.println("序列化成功 (符合预期): " + resultingJson);
} catch (Exception e) {
System.err.println("序列化失败 (不应该发生):" + e.getMessage());
}
}
}运行结果:
--- 示例1:必填字段为null ---
序列化失败,符合预期:BigJsonDto实例无效:存在必填字段为null。
--- 示例2:所有必填字段都有值 ---
序列化成功 (符合预期): {"field1":"value1","field2":"value2","field3":"value3","fieldN":"valueN"}从输出可以看出,当invalidDto的field1为null时,mapper.writeValueAsString(invalidDto)抛出了我们自定义的JsonGenerationException,成功阻止了序列化。而validDto则被正常序列化。
通过自定义Jackson序列化器,我们获得了对序列化过程的细粒度控制,能够实现更严格的数据完整性校验,确保只有符合业务规则的对象才能被成功序列化为JSON。这种方法为处理复杂的数据校验场景提供了强大的工具。
以上就是Jackson序列化:当必填字段为空时阻止对象序列化的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号