结论先行

FastJSON 的 SerializeFilter 接口通过 动态拦截和修改序列化过程,可实现字段名重命名、敏感数据脱敏、字段过滤等高级功能。其核心子接口包括 PropertyPreFilterValueFilterNameFilterContextValueFilter,分别解决不同场景需求。

接口选择指南

过滤器类型 适用场景 示例
PropertyPreFilter 按字段名黑名单/白名单过滤 过滤临时字段 _temp
ValueFilter 动态修改字段值 手机号脱敏
NameFilter 统一字段命名风格 驼峰转下划线
ContextValueFilter 根据类或字段类型差异化处理 仅对特定类脱敏

文章持续更新,可以微信搜一搜「 半个脑袋儿 」第一时间阅读

完整流程图

graph TD
A[开始序列化] --> B[遍历对象字段]
B --> C{是否通过 PropertyPreFilter?}
C -->|否| D[丢弃字段]
C -->|是| E[应用 NameFilter 修改字段名]
E --> F[应用 ValueFilter/ContextValueFilter 修改值]
F --> G[生成最终键值对]
G --> H[继续下一个字段]
H --> B
H --> I[生成完整 JSON]

一、PropertyPreFilter:按字段名过滤

场景

过滤对象中不符合命名规范的字段(如临时字段 _tempData)。

示例代码
public class PropertyPreFilterDemo {
public static void main(String[] args) {
User user = new User("admin", "_tempData123", "13800138000"); // 创建过滤器:过滤以 "_" 开头的字段
PropertyPreFilter filter = (serializer, source, name) ->
!name.startsWith("_"); // 返回 true 表示保留该字段 String json = JSON.toJSONString(user, filter);
System.out.println(json);
// 输出:{"name":"admin","phone":"13800138000"}
} static class User {
private String name;
private String _tempData; // 需要过滤的字段
private String phone; public User(String name, String _tempData, String phone) {
this.name = name;
this._tempData = _tempData;
this.phone = phone;
}
// 省略 getter/setter
}
}
源码解析
  1. PropertyPreFilter 接口定义

    public interface PropertyPreFilter extends SerializeFilter {
    boolean apply(JSONSerializer serializer, Object object, String name);
    }
    • name:当前字段名。
    • 返回 true 表示保留字段,false 表示过滤。
  2. Lambda 实现

    (serializer, source, name) -> !name.startsWith("_")
    • 直接通过字段名判断是否保留,简洁高效。

二、ValueFilter:动态修改字段值

场景

对敏感数据(如手机号、身份证号)进行脱敏处理。

示例代码(含健壮性校验)
public class ValueFilterDemo {
public static void main(String[] args) {
User user = new User("李四", "510123199001011234", "13812345678"); // 创建 ValueFilter:脱敏身份证和手机号
ValueFilter filter = new ValueFilter() {
@Override
public Object process(Object object, String name, Object value) {
if (value == null) return null; // 处理空值 switch (name) {
case "idCard":
if (value instanceof String && ((String) value).length() >= 15) {
String idCard = (String) value;
return idCard.substring(0, 6) + "********" + idCard.substring(14);
}
break;
case "phone":
if (value instanceof String && ((String) value).length() == 11) {
String phone = (String) value;
return phone.substring(0, 3) + "****" + phone.substring(7);
}
break;
}
return value;
}
}; String json = JSON.toJSONString(user, filter);
System.out.println(json);
// 输出:{"idCard":"510123********1234","name":"李四","phone":"138****5678"}
} static class User {
private String name;
private String idCard;
private String phone; // 省略构造方法和 getter/setter
}
}
源码解析
  1. ValueFilter 接口定义

    public interface ValueFilter extends SerializeFilter {
    Object process(Object object, String name, Object value);
    }
    • object:当前序列化的对象实例(可获取其他字段值)。
    • value:当前字段的原始值,需返回修改后的值。
  2. 健壮性设计

    • 校验 value 是否为 null,避免空指针异常。
    • 校验数据类型和长度,防止无效脱敏。

三、NameFilter:字段名重命名

场景

统一 JSON 字段命名风格(如 Java 驼峰命名 → JSON 下划线命名)。

示例代码
public class NameFilterDemo {
public static void main(String[] args) {
Product product = new Product(1001, "MacBook Pro", 14999.99); // 创建 NameFilter:驼峰转下划线
NameFilter filter = (object, name, value) -> {
String regex = "([a-z])([A-Z]+)";
String replacement = "$1_$2";
return name.replaceAll(regex, replacement).toLowerCase();
}; String json = JSON.toJSONString(product, filter);
System.out.println(json);
// 输出:{"product_id":1001,"product_name":"MacBook Pro","product_price":14999.99}
} static class Product {
private Integer productId;
private String productName;
private Double productPrice; // 省略构造方法和 getter/setter
}
}
源码解析
  1. 正则表达式处理

    • ([a-z])([A-Z]+):匹配驼峰命名中的大写字母位置。
    • $1_$2:在大写字母前插入下划线。
    • toLowerCase():统一转为小写,符合下划线风格。

四、ContextValueFilter:上下文感知的字段处理

场景

根据不同对象类型动态脱敏(如 User 类手机号脱敏,Order 类不脱敏)。

示例代码
public class ContextValueFilterDemo {
public static void main(String[] args) {
User user = new User("王五", "13800001234");
Order order = new Order(2001, "13800001234"); // 创建 ContextValueFilter:仅对 User 类手机号脱敏
ContextValueFilter filter = new ContextValueFilter() {
@Override
public Object process(
BeanContext context, Object object, String name, Object value) { // 获取字段所属的类
Class<?> clazz = context.getBeanClass(); if (clazz == User.class && "phone".equals(name)) {
return handlePhone((String) value);
}
return value;
} private String handlePhone(String phone) {
if (phone != null && phone.length() == 11) {
return phone.substring(0, 3) + "****" + phone.substring(7);
}
return phone;
}
}; System.out.println("User JSON: " + JSON.toJSONString(user, filter));
// 输出:{"name":"王五","phone":"138****1234"} System.out.println("Order JSON: " + JSON.toJSONString(order, filter));
// 输出:{"orderId":2001,"contactPhone":"13800001234"}
} static class User {
private String name;
private String phone;
// 省略构造方法和 getter/setter
} static class Order {
private Integer orderId;
private String contactPhone;
// 省略构造方法和 getter/setter
}
}
源码解析
  1. BeanContext 核心方法
    public interface BeanContext {
    Class<?> getBeanClass(); // 获取当前对象的类
    Method getMethod(); // 获取当前字段的 getter 方法
    Field getField(); // 获取当前字段的反射对象
    }
    • 可通过 getBeanClass() 判断对象类型,实现差异化处理。

五、组合过滤器与性能优化

场景

同时使用多个过滤器(如脱敏 + 字段名转换)。

示例代码
public class CombinedFilterDemo {
public static void main(String[] args) {
User user = new User("赵六", "13912345678"); // 过滤器1:手机号脱敏
ValueFilter phoneFilter = (obj, name, value) ->
"phone".equals(name) ? "***-****-" + ((String) value).substring(7) : value; // 过滤器2:字段名转下划线
NameFilter nameFilter = (obj, name, value) ->
name.replaceAll("([A-Z])", "_$1").toLowerCase(); // 组合使用两个过滤器
String json = JSON.toJSONString(user, phoneFilter, nameFilter);
System.out.println(json);
// 输出:{"name":"赵六","phone":"***-****-5678"}
} static class User {
private String name;
private String phone;
// 省略构造方法和 getter/setter
}
}
最佳实践
  1. 性能优化

    • 避免在过滤器中执行耗时操作(如数据库查询)。
    • 优先使用 @JSONField 处理静态规则,过滤器处理动态逻辑。
  2. 全局过滤器注册

    // 全局注册 PhoneFilter,对所有序列化生效
    SerializeConfig.getGlobalInstance().addFilter(User.class, phoneFilter);
    • 谨慎使用全局过滤器,避免影响其他模块。

    文章持续更新,可以微信搜一搜「 半个脑袋儿 」第一时间阅读

FastJSON序列化扩展接口与特性详解的更多相关文章

  1. Git使用总结 Asp.net生命周期与Http协议 托管代码与非托管代码的区别 通过IEnumerable接口遍历数据 依赖注入与控制反转 C#多线程——优先级 AutoFac容器初步 C#特性详解 C#特性详解 WPF 可触摸移动的ScrollViewer控件 .NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用

    一,原理 首先,我们要明白Git是什么,它是一个管理工具或软件,用来管理什么的呢?当然是在软件开发过程中管理软件或者文件的不同版本的工具,一些作家也可以用这个管理自己创作的文本文件,由Linus开发的 ...

  2. Java基础学习总结(33)——Java8 十大新特性详解

    Java8 十大新特性详解 本教程将Java8的新特新逐一列出,并将使用简单的代码示例来指导你如何使用默认接口方法,lambda表达式,方法引用以及多重Annotation,之后你将会学到最新的API ...

  3. C#中的 特性 详解(转载)

    本篇幅转载于:http://www.cnblogs.com/rohelm/archive/2012/04/19/2456088.html C#中特性详解 特性提供了功能强大的方法,用于将元数据或声明信 ...

  4. iOS开发——高级特性&Runtime运行时特性详解

    Runtime运行时特性详解 本文详细整理了 Cocoa 的 Runtime 系统的知识,它使得 Objective-C 如虎添翼,具备了灵活的动态特性,使这门古老的语言焕发生机.主要内容如下: 引言 ...

  5. 《Android群英传》读书笔记 (5) 第十一章 搭建云端服务器 + 第十二章 Android 5.X新特性详解 + 第十三章 Android实例提高

    第十一章 搭建云端服务器 该章主要介绍了移动后端服务的概念以及Bmob的使用,比较简单,所以略过不总结. 第十三章 Android实例提高 该章主要介绍了拼图游戏和2048的小项目实例,主要是代码,所 ...

  6. C#各个版本中的新增特性详解

    序言 自从2000年初期发布以来,c#编程语言不断的得到改进,使我们能够更加清晰的编写代码,也更加容易维护我们的代码,增强的功能已经从1.0搞到啦7.0甚至7.1,每一次改过都伴随着.NET Fram ...

  7. Android群英传笔记——第十二章:Android5.X 新特性详解,Material Design UI的新体验

    Android群英传笔记--第十二章:Android5.X 新特性详解,Material Design UI的新体验 第十一章为什么不写,因为我很早之前就已经写过了,有需要的可以去看 Android高 ...

  8. 单元测试系列之十一:Jmockit之mock特性详解

    本文是Jmockit学习过程中,根据官网所列的工具特性进行解读. 1.调用次数约束(Invocation count constraints) 可以通过调用计数约束来指定预期和/或允许匹配给定期望的调 ...

  9. Java9 新特性 详解

    作者:木九天   <   Java9 新特性 详解  > Java9 新特性 详解 摘要: 1.目录结构 2.repl工具 jShell命令 3.模块化 4.多版本兼容jar包 5.接口方 ...

  10. Atitit.jdk java8的语法特性详解 attilax 总结

    Atitit.jdk java8的语法特性详解 attilax 总结 1.1. 类型推断这个特别有趣的.鲜为人知的特性1 2. Lambda1 2.1. 内部迭代意味着改由Java类库来进行迭代,而不 ...

随机推荐

  1. Q: 远程ssh登录不上

    1.密码没有问题 .2.防火墙没有禁用端口.3.ssh服务已安装且开启一般是ssh配置文件问题打开ssh配置文件 vim /etc/ssh/sshd_config ################## ...

  2. 【FAQ】HarmonyOS SDK 闭源开放能力 —Live View Kit (1)

    1.问题描述: 客户端创建实况窗后,通过Push kit更新实况窗内容,这个过程是自动更新的还是客户端解析push消息数据后填充数据更新?客户端除了接入Push kit和创建实况窗还需要做什么工作? ...

  3. ESP32-S3-WROOM-1-N16R8

    ESP32-S3-WROOM-1-N16R8 基于立创实战派S3 参考链接:实战派开发板S3介绍 | 立创开发板技术文档中心 硬件和代码,(大部分图片)基于立创文档,在此基础上进行个人的学习记录和理解 ...

  4. Scala Set集合 元素唯一,无序

    package com.wyh.day01 /** * Set集合 * 唯一,无序 * * Set中大部分方法与List一致,但是不可以进行排序 */ object ScalaSet { def ma ...

  5. 关于我在使用Steamlit中碰到的问题及解决方案总结

    Steamlit 并不支持一个可以预览本地文件的路径选择器(并不上传文件) 解决方案:使用 Python 自带的 tkinter 来完成 参考:[Streamlit 选择文件夹的曲折方案]Stream ...

  6. 洛谷P1191 矩形 题解

    笛卡尔树的介绍 笛卡尔树,是一种二叉搜索树,它满足如下条件: 每个节点的编号满足二叉搜索树的性质. 每个节点的权值满足小根堆或大根堆的性质. 大概是这个样子: 笛卡尔树的建树 请看这里. 笛卡尔树的用 ...

  7. CF2018C Tree Pruning

    分析 好像官方题解是反向求解的,这里提供一个正向求解的思路,即直接求出最后所有叶节点到根的距离相同为 \(x\) 时需要删除的结点数 \(ans_x\) . 如果我们最后到根的相同距离为 \(x\), ...

  8. deepseek:如何用php写微信公众号订阅回复事件

    以下是使用 PHP 重写的微信公众号订阅事件回复的示例代码.这个代码实现了用户订阅(关注)公众号时,自动回复一条欢迎消息. PHP 实现代码 <?php // 微信公众平台的Token defi ...

  9. C#如何使用HttpClient对大文件进行断点上传和下载

    什么是Http的断点上传和下载 断点上传:在向服务商上传大文件的时候,将一个大的文件拆分成多个小的文件,每个文件通过单独的Http请求上传给服务器. 断点下载:在向服务器请求下载一个大的资源文件的时候 ...

  10. mysql 卸载安装教程链接

    https://blog.csdn.net/weixin_56952690/article/details/129678685 https://blog.51cto.com/u_16213646/70 ...