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在最后添加额外内容

值得特别注意的是,当多个过滤器同时存在时,它们的执行顺序如下:

  1. PropertyPreFilter(按字段名预过滤)
  2. PropertyFilter(按字段名+字段值过滤)
  3. NameFilter(修改字段名)
  4. ValueFilter(修改字段值)
  5. BeforeFilter(在对象所有属性之前执行)
  6. AfterFilter(在对象所有属性之后执行)

其中 BeforeFilterAfterFilter 仅在序列化的是普通 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:选择性序列化

PropertyFilterPropertyPreFilter 的基础上更进一步——它同时基于字段名和字段值进行综合判断。这意味着你可以根据字段的实际内容动态决定是否输出。

典型场景

  • 过滤掉值为 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方法

BeforeFilterAfterFilter 用于在整个对象的序列化之前和之后向 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"} 

 重要注意事项BeforeFilterAfterFilter 仅在传入的是 JavaBean 对象时才会生效。如果传入的是 ListMap 等类型,内部由 MapSerializer 等专用序列化器处理,这两个过滤器的逻辑将不会被执行。

组合使用与执行顺序分析

多个过滤器可以同时传递给 JSON.toJSONString 方法,它们按照前文所述的顺序依次执行。

以下示例组合了 PropertyPreFilterNameFilterValueFilter

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); 

执行流程如下:

  1. PropertyPreFilter 先根据字段名 password 决定跳过,password 字段的 getter 方法不会被调用;
  2. NameFilterid 字段名修改为 userId
  3. ValueFilter 对所有非 null 字段值(如 null)进行转换。

理解这一链条式的执行顺序,可以精确控制序列化输出的每一个环节。

效率验证:性能影响可忽略

很多开发者担心使用过滤器会带来性能开销。实际测试表明,在循环十万次序列化操作时,使用自定义过滤器与不使用之间仅相差约 1 秒钟,这种差距在绝大多数业务场景中可以完全忽略不计。

过滤器内部的核心逻辑非常轻量——主要是通过 instanceof 判断过滤器类型后添加到对应队列中,以及在序列化遍历过程中按顺序调用。真正的性能瓶颈通常在于:

  • getter 方法内部是否包含耗时操作(如数据库查询、远程调用);
  • 序列化对象图的大小和深度;
  • ValueFilter 内部是否有复杂的字符串拼接或正则匹配。

建议在 ValueFilter 中尽量避免过于复杂的运算,对于需要大量反射操作的场景,可考虑将反射结果缓存起来以优化性能。

注意事项与避坑指南

  1. 过滤器不为 nullJSON.toJSONString 中传入的过滤器不能为 null,否则 Fastjson 内部可能会在添加过滤器链时报空指针异常。实际使用中建议在传递前进行判空处理。

  2. 重复作用域:多个过滤器同时对同一个字段进行修改时,生效顺序遵循 PropertyPreFilterPropertyFilterNameFilterValueFilterBeforeFilterAfterFilter。这一顺序是不可逆的,设计过滤器时需要充分考虑不同过滤器之间的相互影响。

  3. 对象类型差异BeforeFilter / AfterFilter 仅对普通 JavaBean 有效。对于 MapList、数组等类型,如需添加“头尾”内容,需要考虑在 JavaBean 层统一封装后再进行序列化。

  4. @JSONField 与过滤器的优先级@JSONField 注解的 serialize 属性优先级最高,会先行决定字段是否可序列化。因此,如果通过 @JSONField(serialize = false) 标注了某个字段,即使 PropertyFilter 返回 true,该字段仍不会出现在输出中。

  5. AutoType 安全性:Fastjson 1.x 版本的 AutoType 功能曾曝出多个安全漏洞。在使用序列化过滤器时,尤其是在处理来自不可信源的数据时,建议始终保持 AutoType 功能关闭状态,或采用白名单机制对反序列化的类进行限制。

结语

Fastjson 的序列化过滤器体系设计精巧且覆盖面广,提供了从字段筛选、字段名修改、字段值转换,到额外内容追加的完整能力。合理运用这些过滤器,可以在不入侵业务代码的前提下,实现高度灵活的 JSON 序列化逻辑,极大地提高代码的可维护性和可扩展性。