第5篇:自定义序列化器与反序列化器:突破默认逻辑
在使用 Jackson 处理 JSON 与 Java 对象的转换时,默认序列化/反序列化逻辑能满足大部分常规场景(如String、Integer、POJO的直接映射)。但在实际业务中,我们常遇到默认逻辑无法覆盖的需求——比如手机号脱敏、敏感字段加密、JSON数组转自定义对象等。此时,自定义序列化器与反序列化器就成为突破限制的核心手段。
一、为什么需要自定义?默认逻辑的局限性
Jackson 的默认处理逻辑基于“字段名匹配+类型自动映射”,但面对以下业务场景时会失效:
- 敏感数据脱敏/加密:如用户手机号、身份证号,不能直接以明文返回,需要将中间几位替换为
*
(脱敏),或通过 AES 加密后传输; - 特殊格式转换:如后端存储的“时间戳”需要转为前端可读的“yyyy-MM-dd HH:mm:ss”格式,或前端传递的“JSON 数组字符串”需要转为后端自定义的
TagList
对象; - 非常规类型映射:如 JSON 中的
String
类型(如“1,2,3”)需要转为 Java 中的List<Integer>
,或 JSON 中的Object
需要根据字段动态转为不同子类; - 空值特殊处理:默认情况下
null
字段会被忽略或序列化为null
,但业务可能要求将null
转为空字符串""
或默认值(如 0)。
举个直观例子:若 Java 对象中userPhone
字段是“13812345678”,默认序列化会直接输出该明文;但业务要求返回“138****5678”,此时必须通过自定义序列化器实现。
二、自定义序列化器:控制 JSON 的“输出格式”
序列化的核心是将 Java 对象转为 JSON 数据,自定义序列化器的目标是控制某类字段/类型的 JSON 输出形式。Jackson 中自定义序列化器的核心步骤是:继承StdScalarSerializer
(处理标量类型,如 String、Integer),并重写serialize()
方法。
2.1 核心原理与步骤
- 继承基类:根据需处理的类型选择基类,最常用的是
StdScalarSerializer<T>
(T 为目标 Java 类型,如 String、Long);若处理复杂 POJO,可继承StdSerializer<T>
。 - 重写
serialize()
方法:该方法定义了“Java 对象 → JSON”的转换逻辑,核心参数包括:value
:待序列化的 Java 对象(如手机号字符串);gen
:JsonGenerator
对象,用于生成 JSON 节点(如通过gen.writeString()
输出最终的 JSON 字符串);provider
:SerializerProvider
,提供序列化上下文(如获取配置、其他序列化器)。
- 指定构造方法:必须显式调用父类构造方法,指定目标类型(如
super(String.class)
)。
2.2 实战:手机号脱敏序列化器
需求:将 Java 中的手机号字符串(如“13812345678”)序列化为 JSON 时,自动替换中间 4 位为*
,输出“138****5678”。
步骤1:实现自定义序列化器
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;import java.io.IOException;/*** 手机号脱敏序列化器:将中间4位替换为**/
public class PhoneDesensitizeSerializer extends StdScalarSerializer<String> {// 显式指定处理的类型为Stringpublic PhoneDesensitizeSerializer() {super(String.class);}/*** 核心序列化逻辑* @param value 待序列化的手机号(如"13812345678")* @param gen JsonGenerator:用于生成JSON输出* @param provider 序列化上下文*/@Overridepublic void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {// 1. 校验手机号格式(简化逻辑,实际需更严谨)if (value == null || value.length() != 11) {gen.writeString(value); // 非11位手机号,直接输出原值