Fastjson1 序列化过滤器
为什么需要序列化过滤器?
在实际项目中,JSON 序列化并非总是简单的“对象转 JSON 字符串”。我们常常面临各种定制化需求:
- 某些字段需要根据业务条件决定是否输出(如敏感数据、用户权限);
- 某个属性的值需要在序列化时统一做格式处理或类型转换;
- 想要给最终生成的 JSON 动态添加额外信息(如数据签名、时间戳);
- 需要修改 JSON 的字段名以达到 API 版本兼容或脱敏目的。
Fastjson 内置的 @JSONField 注解虽然能解决部分问题,但编程式扩展——也就是序列化过滤器(SerializeFilter)——才是实现上述所有定制化需求的真正利器。本文将系统介绍 Fastjson 1.x 序列化过滤器家族的全部成员,并结合大量可运行示例帮助读者掌握其用法。
序列化过滤器体系概述
SerializeFilter 是 Fastjson 提供的一组编程式扩展接口,通过自定义实现接口并传递给序列化方法,可以在序列化过程中对字段名、字段值、输出范围等内容进行精细控制。
Fastjson 1.x 支持 6 种 SerializeFilter,覆盖了序列化场景下最常见的需求:
| 过滤器 | 作用 |
|---|---|
PropertyPreFilter | 仅根据字段名判断是否序列化 |
PropertyFilter | 根据字段名+字段值综合判断是否序列化 |
NameFilter | 修改 Key(字段名) |
ValueFilter | 修改 Value(字段值) |
BeforeFilter | 在最前添加额外内容 |
AfterFilter | 在最后添加额外内容 |
值得特别注意的是,当多个过滤器同时存在时,它们的执行顺序如下:
PropertyPreFilter(按字段名预过滤)PropertyFilter(按字段名+字段值过滤)NameFilter(修改字段名)ValueFilter(修改字段值)BeforeFilter(在对象所有属性之前执行)AfterFilter(在对象所有属性之后执行)
其中 BeforeFilter 和 AfterFilter 仅在序列化的是普通 JavaBean 对象时才会执行;Map 等类型由内部专用序列化器处理,逻辑有所不同。理解这一执行顺序对于调试多过滤器场景至关重要。
PropertyPreFilter:按字段名预过滤
PropertyPreFilter 是所有过滤器中最“前端”的一个。它在调用字段的 getter 方法之前执行判断,仅根据字段名决定是否输出当前字段。这一设计的好处是:对于不需要输出的字段,连 getter 都不会被调用,从而可以避免因 getter 方法中可能存在的副作用(如日志记录、状态变更)而产生的潜在问题。
典型场景:API 根据不同用户角色返回不同字段;为前端接口返回有限的字段列表。
示例:只输出指定字段
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.PropertyPreFilter;
public class MyPreFilter implements PropertyPreFilter {
@Override
public boolean apply(JSONSerializer serializer, Object object, String fieldName) {
// 只序列化 id和 name字段
return "id".equals(fieldName) || "name".equals(fieldName);
}
}
使用方式非常简洁:
User user = new User(1L, "张三", "12345678", "北京市朝阳区");
String json = JSON.toJSONString(user, new MyPreFilter());
// 输出:{"id":1,"name":"张三"}
SimplePropertyPreFilter 是 Fastjson 官方提供的一个开箱即用实现,可以直接通过构造方法传入需要包含的字段名,极大简化了日常使用:
SimplePropertyPreFilter filter = new SimplePropertyPreFilter("id", "name");
String json = JSON.toJSONString(user, filter);
在实际项目中,还可以通过排除字段的方式灵活使用:
String[] keys = {"id", "name", "address"};
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(User.class, keys);
String json = JSON.toJSONString(user, filter);
System.out.println(json);//输出 {"address":"北京市朝阳区","id":1,"name":"张三"}
PropertyFilter:选择性序列化
PropertyFilter 在 PropertyPreFilter 的基础上更进一步——它同时基于字段名和字段值进行综合判断。这意味着你可以根据字段的实际内容动态决定是否输出。
典型场景:
- 过滤掉值为
null的字段; - 根据阈值过滤(如只输出积分 ≥ 100 的用户);
- 过滤敏感数据(如包含特定内容时跳过)。
示例:按字段值过滤
PropertyFilter 的定义较为直观,通过 apply 方法返回 true 表示保留,返回 false 表示过滤:
// 定义类
@Data
public class User {
private Long id;
private String name;
private String password;
private String address;
private Integer age;
private Integer score;
}
// 定义过滤器
public class MyPreFilter implements PropertyFilter {
@Override
public boolean apply(Object source, String name, Object value) {
// source: 当前正在序列化的对象(这里是 user 对象)
// name: 字段名
// value: 字段值
// 条件1:过滤掉 age 字段(无论值是什么都不输出)
if ("age".equals(name)) {
System.out.println("过滤掉 age 字段");
return false;
}
// 条件2:过滤掉值为 null 的字段
if (value == null) {
System.out.println("过滤掉 null 值字段: " + name);
return false;
}
// 返回 true 表示保留该字段
return true;
}
}
// 调用方法
PropertyFilter filter = new MyPreFilter();
User user = new User();
user.setId(1L);
user.setName("张三");
user.setAge(25);
user.setScore(150); // 积分
String json = JSON.toJSONString(user, filter);
System.out.println(json);
输出值为:
过滤掉 null 值字段: address
过滤掉 age 字段
过滤掉 null 值字段: password
{"id":1,"name":"张三","score":150}
按数值范围过滤
public class MyPreFilter implements PropertyFilter {
@Override
public boolean apply(Object source, String name, Object value) {
if ("score".equals(name)) {
int score = ((Integer) value).intValue();
return score >= 100; // 只输出分数 ≥ 100 的记录
}
return true;
}
}
PropertyFilter scoreFilter = new MyPreFilter();
JSON.toJSONString(obj, scoreFilter);
需要特别注意:PropertyFilter 在调用 getter 方法之前执行判断,但获取字段值时仍会触发 getter。apply 方法的 value 参数即为 getter 返回的值。如果 getter 方法内部有复杂逻辑(如数据库查询、远程调用),建议优先考虑 PropertyPreFilter,以避免不必要的开销和潜在的副作用。
NameFilter:动态修改字段名
NameFilter 允许你在序列化过程中修改 JSON 字段的 Key 值,非常适合在实现多版本 API 兼容、字段脱敏或统一命名规范时使用。
典型场景:
- API 升级时用“别名”过渡;
- 动态拼接前缀/后缀;
- 实现驼峰命名与下划线命名互转等规范统一。
示例:字段名统一加前缀
// 定义类
@Data
public class User {
private Long id;
private String name;
private String password;
private String address;
private Integer age;
private Integer score;
}
// 定义NameFilter过滤器
public class MyPreFilter implements NameFilter {
@Override
public String process(Object o, String name, Object value) {
return "user_" + name;
}
}
// 调用方法
NameFilter filter = new MyPreFilter();
User user = new User();
user.setId(1L);
user.setName("张三");
user.setAge(25);
user.setScore(150); // 积分
String json = JSON.toJSONString(user, filter);
System.out.println(json);// 输出 {"user_age":25,"user_id":1,"user_name":"张三","user_score":150}
Fastjson 内置了一个实用的 PascalNameFilter,可以将字段名统一转换为首字母大写的 Pascal 风格,在日常开发中可以进一步封装以灵活应对 API 命名规范的统一调整。
ValueFilter:灵活修改字段值
ValueFilter 在序列化时对值(Value)进行修改,是实际项目中使用频率最高的过滤器之一,应用场景极其广泛:
- 将
null值转换为空字符串""或自定义占位符; - 对敏感信息(手机号、身份证)进行脱敏处理;
- 将
BigDecimal转为String避免前端解析精度丢失; - 统一格式化日期时间。
示例:null 值转换
// 定义类
@Data
public class User {
private Long id;
private String name;
private String password;
private String address;
private Integer age;
private Integer score;
}
// 定义ValueFilter过滤器
public class MyPreFilter implements ValueFilter {
// 将null转""或者
@Override
public Object process(Object object, String name, Object value) {
return value == null ? "" : value;
}
}
// 调用方法
ValueFilter filter = new MyPreFilter();
User user = new User();
user.setId(1L);
user.setAge(25);
user.setScore(150); // 积分
String json = JSON.toJSONString(user, filter);
System.out.println(json); // 输出 {"address":"","age":25,"id":1,"name":"","password":"","score":150}
进阶:结合注解实现智能转换
在实际工程中,通常需要结合自定义注解来实现更智能的转换逻辑。例如,将带有 @FieldToString 注解的 BigDecimal 字段在序列化时自动转换为字符串,以解决 JavaScript 解析大数时精度丢失的问题:
public BeanPropertyFilter implements ValueFilter {
@Override
public Object process(Object obj, String, Object value) {
try {
Field field = obj.getClass().getDeclaredField(name);
FieldToString annotation = field.getAnnotation(FieldToString.class);
if (annotation != null && annotation.value().equals("true") && value != null) {
return value + ""; // 转为字符串
}
} catch (Exception e) {
return value;
}
return value;
}
}
// 测试代码 D {
@FieldToString
private BigDecimal bigDecimal;
private BigDecimal bigDecimal2;
private double d;
// getters/setters 省略
}
// 执行序列化
System.out.println(JSON.toJSONString(d, new BeanPropertyFilter()));
// 输出:{"bigDecimal":"0.315675","bigDecimal2":0.316767,"d":2222}
BeforeFilter 与 AfterFilter方法
BeforeFilter 和 AfterFilter 用于在整个对象的序列化之前和之后向 JSON 输出中添加额外内容。
BeforeFilter:在写入对象所有属性之前执行,可通过 writeKeyValue(key, value) 方法添加额外的键值对,非常适合用来添加时间戳、API 版本号、请求追踪 ID 等元信息。
AfterFilter:在写入对象所有属性之后执行,适用于添加数据签名、校验码或尾标等补充性内容。
示例:添加时间戳和签名
// 定义类
@Data
public class User {
private Long id;
private String name;
private String password;
private String address;
private Integer age;
private Integer score;
}
// 定义 BeforeFilter 过滤器
import com.alibaba.fastjson.serializer.BeforeFilter;
public class CustomBeforeFilter extends BeforeFilter {
@Override
public void writeBefore(Object object) {
writeKeyValue("timestamp", System.currentTimeMillis());
}
}
// 定义 AfterFilter 过滤器
import com.alibaba.fastjson.serializer.AfterFilter;
public class CustomAfterFilter extends AfterFilter {
@Override
public void writeAfter(Object object) {
writeKeyValue("signature", generateSignature(object));
}
// 签名生成方法的具体实现
private String generateSignature(Object object) {
if (object == null) {
return "null";
}
// 方式1:基于对象 toString 的简单签名
String data = object.toString();
return simpleHash(data);
// 方式2:基于对象字段值的 MD5 签名(更安全)
// return generateMD5Signature(object);
// 方式3:基于 JSON 字符串的签名(最常用)
// return generateJSONSignature(object);
}
// 简单的哈希签名
private String simpleHash(String input) {
return Integer.toHexString(input.hashCode());
}
}
// 调用方法
User user = new User();
user.setId(1L);
user.setAge(25);
user.setScore(150); // 积分
// 将多个过滤器放入数组
SerializeFilter[] filters = {
new CustomBeforeFilter(),
new CustomAfterFilter()
};
String json = JSON.toJSONString(user, filters);
System.out.println(json);
// 输出 {"timestamp":1778810771666,"age":25,"id":1,"score":150,"signature":"9e6ec471"}
重要注意事项:BeforeFilter 和 AfterFilter 仅在传入的是 JavaBean 对象时才会生效。如果传入的是 List、Map 等类型,内部由 MapSerializer 等专用序列化器处理,这两个过滤器的逻辑将不会被执行。
组合使用与执行顺序分析
多个过滤器可以同时传递给 JSON.toJSONString 方法,它们按照前文所述的顺序依次执行。
以下示例组合了 PropertyPreFilter、NameFilter 和 ValueFilter:
PropertyPreFilter preFilter = (serializer, object,) -> !"password".equals(name);
NameFilterFilter = (object,, value) ->.equals("id") ? "userId" :;
ValueFilter valueFilter = (object,, value) -> value == null ? "" : value;
String json = JSON.toJSONString(user, preFilter,Filter, valueFilter);
执行流程如下:
PropertyPreFilter先根据字段名password决定跳过,password字段的 getter 方法不会被调用;NameFilter将id字段名修改为userId;ValueFilter对所有非 null 字段值(如null)进行转换。
理解这一链条式的执行顺序,可以精确控制序列化输出的每一个环节。
效率验证:性能影响可忽略
很多开发者担心使用过滤器会带来性能开销。实际测试表明,在循环十万次序列化操作时,使用自定义过滤器与不使用之间仅相差约 1 秒钟,这种差距在绝大多数业务场景中可以完全忽略不计。
过滤器内部的核心逻辑非常轻量——主要是通过 instanceof 判断过滤器类型后添加到对应队列中,以及在序列化遍历过程中按顺序调用。真正的性能瓶颈通常在于:
- getter 方法内部是否包含耗时操作(如数据库查询、远程调用);
- 序列化对象图的大小和深度;
ValueFilter内部是否有复杂的字符串拼接或正则匹配。
建议在 ValueFilter 中尽量避免过于复杂的运算,对于需要大量反射操作的场景,可考虑将反射结果缓存起来以优化性能。
注意事项与避坑指南
过滤器不为 null:
JSON.toJSONString中传入的过滤器不能为null,否则 Fastjson 内部可能会在添加过滤器链时报空指针异常。实际使用中建议在传递前进行判空处理。重复作用域:多个过滤器同时对同一个字段进行修改时,生效顺序遵循
PropertyPreFilter→PropertyFilter→NameFilter→ValueFilter→BeforeFilter→AfterFilter。这一顺序是不可逆的,设计过滤器时需要充分考虑不同过滤器之间的相互影响。对象类型差异:
BeforeFilter/AfterFilter仅对普通 JavaBean 有效。对于Map、List、数组等类型,如需添加“头尾”内容,需要考虑在 JavaBean 层统一封装后再进行序列化。@JSONField与过滤器的优先级:@JSONField注解的serialize属性优先级最高,会先行决定字段是否可序列化。因此,如果通过@JSONField(serialize = false)标注了某个字段,即使PropertyFilter返回true,该字段仍不会出现在输出中。AutoType 安全性:Fastjson 1.x 版本的 AutoType 功能曾曝出多个安全漏洞。在使用序列化过滤器时,尤其是在处理来自不可信源的数据时,建议始终保持 AutoType 功能关闭状态,或采用白名单机制对反序列化的类进行限制。
结语
Fastjson 的序列化过滤器体系设计精巧且覆盖面广,提供了从字段筛选、字段名修改、字段值转换,到额外内容追加的完整能力。合理运用这些过滤器,可以在不入侵业务代码的前提下,实现高度灵活的 JSON 序列化逻辑,极大地提高代码的可维护性和可扩展性。

