Web开发轮子(二)——Fastjson工具类的使用
前言
对于前后端分离的项目或者是微服务之间的接口交互,选择
json
格式的数据来进行交互已经是十分常见的选择,那么熟练掌握1-2款json
转换工具可以在一定程度上帮助我们更好的完成开发,简化我们的开发步骤。
一、说说背景
老习惯,讲工具类之前我们先来聊一下Fastjson的一些题外话。
什么是JSON?
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。相比于“笨重”的xml来说,json
格式数据无疑更加轻巧,也更加便于人们阅读。
有了对json格式数据的需要,市面上也出现了众多json格式数据的处理工具,目前国内用的比较多的是fastjson
和jackson
。尽管fastjson
因为安全漏洞的问题受到一些争议,但其社区活跃度高、简单易用的优势还是成为了国内很多项目json工具库的选择。
下图为json
常见的工具库:
下面就正式进入正题,讲一下fastjson
的使用
二、Fastjson的使用
(一)快速入门使用
步骤一:引入坐标依赖
这里的version
可以根据项目的实际需求来,但建议是尽量使用高版本的依赖,因为1.2.66
之前的版本都被爆出过有安全漏洞的问题。当然了,实际项目中我们最好是有定时扫描依赖包,及时规避依赖漏洞问题的习惯。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
步骤二:创建dto
定义User
类
@Data
public class User {
/**
* 用户名
*/
private String userName;
/**
* 密码
*/
private String password;
/**
* 生日
*/
private Date birthday;
/**
* 注册时间
*/
private LocalDateTime registerTime;
/**
* 地址
*/
private String addr;
}
步骤三:序列化测试
序列化的关键在于使用JSON.toJSONString
这个api进行序列化操作。
@Test
public void serializationTest(){
User user = new User();
user.setUserName("测试");
user.setPassword("123");
user.setBirthday(new Date());
user.setRegisterTime(LocalDateTime.now());
user.setAddr("广西省南宁市");
String jsonStr = JSON.toJSONString(user);
System.out.println("jsonStr = " + jsonStr);
}
我们可以看到,我们的对象被成功序列化为json格式的数据了。
测试结果
步骤四:反序列化操作
反序列化可以通过使用JSON.parseObject
这个api来完成操作。
@Test
public void deSerializationTest(){
String jsonStr = "{\"addr\":\"广西省南宁市\",\"birthday\":1672650565298,\"password\":\"123\",\"registerTime\":\"2023-01-02T17:09:25.345\",\"userName\":\"测试\"}";
User user = JSON.parseObject(jsonStr, User.class);
System.out.println("user = " + user);
}
执行后我们可以看到,json字符串中的属性都已经成功保存至user
对象的属性中了。
(二)序列化的进阶使用
1、输出包含null值的字段
我们先看下面这个例子,当对象的属性中存在值为空的属性,那么fastjson
在序列化的时候会忽略这个属性,也就是说不会对这个属性进行序列化输出。
对于属性值为空的属性,如果我们希望也进行序列化的话,需要加上
SerializerFeature.WriteNullStringAsEmpty
配置
@Test
public void nullTest(){
User user = new User();
user.setUserName("测试");
user.setPassword(null);
user.setBirthday(new Date());
user.setRegisterTime(LocalDateTime.now());
user.setAddr("广西省南宁市");
String jsonStr = JSON.toJSONString(user, SerializerFeature.WriteNullStringAsEmpty);
System.out.println("jsonStr = " + jsonStr);
}
image.png
2、日期格式化
我们从上一节中可以看到,对于Date
和LocalDateTime
类型的数据,fastjson默认是转为时间戳和LocalDateTime
默认的toString格式。如果说我们希望这些属性在转json字符串的时候,可以根据指定时间格式进行格式化的话,可以使用@JSONField
注解来进行解决。使用注解时传入format
属性,来定义我们想要的日期格式。
/**
* 生日
*/
@JSONField(format = "yyyy-MM-dd")
private Date birthday;
/**
* 注册时间
*/
@JSONField(format = "yyyy-MM-dd")
private LocalDateTime registerTime;
我们可以看到,现在json字符串对应的字段值就已经是我们想要格式了。
测试结果
3、解决$ref问题(禁用引用传递)
在一些场景下我们可能会往集合中放入同一个对象,此时输出的json字符串可能并不是如我们期望般的输出多个对象,而是会输出引用传递值。我们不妨看一下下面这个例子:
对于这种问题,我们可以通过指定
DisableCircularReferenceDetect
属性来解决。image.png
4、SerializeFilter的使用
有时候我们想要对输出的字段格式做一些特殊的处理,比如输出的对象属性名都转为大写,这个时候我们就可以使用SerializeFilter
来解决这个问题了。
SerializeFilter
允许我们在序列化前后对字段进行自定义的改造。但需要注意的是,用了SerializeFilter
来做自定义的改造之后,我们原先加在dto字段上的@JSONField
注解会失效。
(三)反序列化的进阶使用
1、泛型处理
@Test
public void genericityTest(){
User user = new User();
user.setUserName("测试");
user.setPassword(null);
user.setBirthday(new Date());
user.setRegisterTime(LocalDateTime.now());
user.setAddr("广西省南宁市");
QiqvResult<User> result = QiqvResult.SUCCESS(user);
String jsonStr = JSON.toJSONString(result);
// 直接使用parseObject并不能有泛型约束
QiqvResult qiqvResult = JSON.parseObject(jsonStr, QiqvResult.class);
Object data = qiqvResult.getData();
// 使用 TypeReference 可以对泛型参数进行转换
QiqvResult<User> userQiqvResult = JSON.parseObject(jsonStr, new TypeReference<QiqvResult<User>>(){});
}
(四)通用配置
通用配置是指下面案例中的配置无论是序列化还是反序列化的场景下都可以使用。
1、美化输出
有时候我们调试接口会需要看一下我们的json参数,但正常转化的json字符串默认不换行的,相对来说可读性较差,我们可以在转化json的时候加上是否美化代码的参数来实现我们的需求。我们一般只会在代码调试或者日志输出的时候才使用这个特性,平时接口交互是不会用这个特性的。
image.png
2、指定属性名和json字符串key的对应关系
有时候我们希望在不修改dto字段名的情况下,给dto字段名起个别名来参与json序列化和反序列化的过程。那我们就可以通过@JSONField
注解的name
属性来解决这个问题
/**
* 地址
*/
@JSONField(name = "address")
private String addr;
加上这个注解后,后续的序列化和反序列化过程中,都会用address
这个字段名来指代addr
字段。
3、忽略指定属性
对象序列化以及反序列化的时候,我们可能会希望某些敏感的字段不参与这个过程,那我们就可以使用@JSONField
注解的serialize
和deserialize
属性来解决这个问题。默认情况下,这两个属性的值都为空,改为false的话就表示这个字段不参与序列化(反序列化)的过程。
/**
* 密码
*/
@JSONField(serialize = false,deserialize = false)
private String password;
测试结果
(四)其他常用的特性
特性名称 | 作用 |
---|---|
QuoteFieldNames | key使用引号 |
UseSingleQuotes | 使用单引号 |
WriteMapNullValue | 输出Map的null值 |
WriteEnumUsingToString | 枚举属性输出toString的结果 |
WriteNullListAsEmpty | List为空则输出[] |
WriteNullStringAsEmpty | String为空则输出”” |
WriteNullNumberAsZero | Number类型为空则输出0 |
WriteNullBooleanAsFalse | Boolean类型为空则输出false |
SortField | 排序字段 |
WriteSlashAsSpecial | 对斜杠’/’进行转义 |
结语
基本上fastjson
工具已经满足了大部分我们需要的操作场景,对于一些比较个性化的需求,我们可以需要考虑自定义序列化(反序列化)的方式来解决这些问题。同时项目引入fastjson依赖也要记得按期进行依赖扫描,及时进行漏洞修复。
参考文章:
JSON官方地址 https://www.json.org/json-zh.html
JSON最佳实践 http://i.kimmking.cn/2017/06/06/json-best-practice/