FastJSON序列化扩展接口与特性详解
结论先行
FastJSON 的 SerializeFilter
接口通过 动态拦截和修改序列化过程,可实现字段名重命名、敏感数据脱敏、字段过滤等高级功能。其核心子接口包括 PropertyPreFilter
、ValueFilter
、NameFilter
和 ContextValueFilter
,分别解决不同场景需求。
接口选择指南
过滤器类型 | 适用场景 | 示例 |
---|---|---|
PropertyPreFilter |
按字段名黑名单/白名单过滤 | 过滤临时字段 _temp |
ValueFilter |
动态修改字段值 | 手机号脱敏 |
NameFilter |
统一字段命名风格 | 驼峰转下划线 |
ContextValueFilter |
根据类或字段类型差异化处理 | 仅对特定类脱敏 |
文章持续更新,可以微信搜一搜「 半个脑袋儿 」第一时间阅读
完整流程图
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
}
}
源码解析
PropertyPreFilter
接口定义public interface PropertyPreFilter extends SerializeFilter {
boolean apply(JSONSerializer serializer, Object object, String name);
}
name
:当前字段名。- 返回
true
表示保留字段,false
表示过滤。
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
}
}
源码解析
ValueFilter
接口定义public interface ValueFilter extends SerializeFilter {
Object process(Object object, String name, Object value);
}
object
:当前序列化的对象实例(可获取其他字段值)。value
:当前字段的原始值,需返回修改后的值。
健壮性设计
- 校验
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
}
}
源码解析
- 正则表达式处理
([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
}
}
源码解析
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
}
}
最佳实践
性能优化
- 避免在过滤器中执行耗时操作(如数据库查询)。
- 优先使用
@JSONField
处理静态规则,过滤器处理动态逻辑。
全局过滤器注册
// 全局注册 PhoneFilter,对所有序列化生效
SerializeConfig.getGlobalInstance().addFilter(User.class, phoneFilter);
- 谨慎使用全局过滤器,避免影响其他模块。
文章持续更新,可以微信搜一搜「 半个脑袋儿 」第一时间阅读
FastJSON序列化扩展接口与特性详解的更多相关文章
- Git使用总结 Asp.net生命周期与Http协议 托管代码与非托管代码的区别 通过IEnumerable接口遍历数据 依赖注入与控制反转 C#多线程——优先级 AutoFac容器初步 C#特性详解 C#特性详解 WPF 可触摸移动的ScrollViewer控件 .NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用
一,原理 首先,我们要明白Git是什么,它是一个管理工具或软件,用来管理什么的呢?当然是在软件开发过程中管理软件或者文件的不同版本的工具,一些作家也可以用这个管理自己创作的文本文件,由Linus开发的 ...
- Java基础学习总结(33)——Java8 十大新特性详解
Java8 十大新特性详解 本教程将Java8的新特新逐一列出,并将使用简单的代码示例来指导你如何使用默认接口方法,lambda表达式,方法引用以及多重Annotation,之后你将会学到最新的API ...
- C#中的 特性 详解(转载)
本篇幅转载于:http://www.cnblogs.com/rohelm/archive/2012/04/19/2456088.html C#中特性详解 特性提供了功能强大的方法,用于将元数据或声明信 ...
- iOS开发——高级特性&Runtime运行时特性详解
Runtime运行时特性详解 本文详细整理了 Cocoa 的 Runtime 系统的知识,它使得 Objective-C 如虎添翼,具备了灵活的动态特性,使这门古老的语言焕发生机.主要内容如下: 引言 ...
- 《Android群英传》读书笔记 (5) 第十一章 搭建云端服务器 + 第十二章 Android 5.X新特性详解 + 第十三章 Android实例提高
第十一章 搭建云端服务器 该章主要介绍了移动后端服务的概念以及Bmob的使用,比较简单,所以略过不总结. 第十三章 Android实例提高 该章主要介绍了拼图游戏和2048的小项目实例,主要是代码,所 ...
- C#各个版本中的新增特性详解
序言 自从2000年初期发布以来,c#编程语言不断的得到改进,使我们能够更加清晰的编写代码,也更加容易维护我们的代码,增强的功能已经从1.0搞到啦7.0甚至7.1,每一次改过都伴随着.NET Fram ...
- Android群英传笔记——第十二章:Android5.X 新特性详解,Material Design UI的新体验
Android群英传笔记--第十二章:Android5.X 新特性详解,Material Design UI的新体验 第十一章为什么不写,因为我很早之前就已经写过了,有需要的可以去看 Android高 ...
- 单元测试系列之十一:Jmockit之mock特性详解
本文是Jmockit学习过程中,根据官网所列的工具特性进行解读. 1.调用次数约束(Invocation count constraints) 可以通过调用计数约束来指定预期和/或允许匹配给定期望的调 ...
- Java9 新特性 详解
作者:木九天 < Java9 新特性 详解 > Java9 新特性 详解 摘要: 1.目录结构 2.repl工具 jShell命令 3.模块化 4.多版本兼容jar包 5.接口方 ...
- Atitit.jdk java8的语法特性详解 attilax 总结
Atitit.jdk java8的语法特性详解 attilax 总结 1.1. 类型推断这个特别有趣的.鲜为人知的特性1 2. Lambda1 2.1. 内部迭代意味着改由Java类库来进行迭代,而不 ...
随机推荐
- Q: 远程ssh登录不上
1.密码没有问题 .2.防火墙没有禁用端口.3.ssh服务已安装且开启一般是ssh配置文件问题打开ssh配置文件 vim /etc/ssh/sshd_config ################## ...
- 【FAQ】HarmonyOS SDK 闭源开放能力 —Live View Kit (1)
1.问题描述: 客户端创建实况窗后,通过Push kit更新实况窗内容,这个过程是自动更新的还是客户端解析push消息数据后填充数据更新?客户端除了接入Push kit和创建实况窗还需要做什么工作? ...
- ESP32-S3-WROOM-1-N16R8
ESP32-S3-WROOM-1-N16R8 基于立创实战派S3 参考链接:实战派开发板S3介绍 | 立创开发板技术文档中心 硬件和代码,(大部分图片)基于立创文档,在此基础上进行个人的学习记录和理解 ...
- Scala Set集合 元素唯一,无序
package com.wyh.day01 /** * Set集合 * 唯一,无序 * * Set中大部分方法与List一致,但是不可以进行排序 */ object ScalaSet { def ma ...
- 关于我在使用Steamlit中碰到的问题及解决方案总结
Steamlit 并不支持一个可以预览本地文件的路径选择器(并不上传文件) 解决方案:使用 Python 自带的 tkinter 来完成 参考:[Streamlit 选择文件夹的曲折方案]Stream ...
- 洛谷P1191 矩形 题解
笛卡尔树的介绍 笛卡尔树,是一种二叉搜索树,它满足如下条件: 每个节点的编号满足二叉搜索树的性质. 每个节点的权值满足小根堆或大根堆的性质. 大概是这个样子: 笛卡尔树的建树 请看这里. 笛卡尔树的用 ...
- CF2018C Tree Pruning
分析 好像官方题解是反向求解的,这里提供一个正向求解的思路,即直接求出最后所有叶节点到根的距离相同为 \(x\) 时需要删除的结点数 \(ans_x\) . 如果我们最后到根的相同距离为 \(x\), ...
- deepseek:如何用php写微信公众号订阅回复事件
以下是使用 PHP 重写的微信公众号订阅事件回复的示例代码.这个代码实现了用户订阅(关注)公众号时,自动回复一条欢迎消息. PHP 实现代码 <?php // 微信公众平台的Token defi ...
- C#如何使用HttpClient对大文件进行断点上传和下载
什么是Http的断点上传和下载 断点上传:在向服务商上传大文件的时候,将一个大的文件拆分成多个小的文件,每个文件通过单独的Http请求上传给服务器. 断点下载:在向服务器请求下载一个大的资源文件的时候 ...
- mysql 卸载安装教程链接
https://blog.csdn.net/weixin_56952690/article/details/129678685 https://blog.51cto.com/u_16213646/70 ...