fastjson2
什么是JSON
网址:https://www.json.org/json-zh.html
JSON是一种轻量级的数据交换格式。它基于ECMAScript的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。
简洁和清晰的层次结构是的JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
JSON语法
使用大括号{ }保存对象,每个对象由若干个数据组成;
每个数据由key:value键值对组成;
数据之间使用逗号,分隔;
使用 \ 进行特殊字符的转义
例如:
{"reason":
"success","result":
[{"id":1,"firstLetter":"A","brandName":"奥迪","brandLogo":"http"},
{"id":2,"firstLetter":"A","brandName":"雷克萨斯","brandLogo":"http"},
{"id":3,"firstLetter":"A","brandName":"奔驰","brandLogo":"ghjvh"}]
JSON的用途
JSON作为一种轻量级的数据格式,它的主要用途是在计算机系统之间进行数据的传递。
JSON作为数据传输的格式,有几个显著的优点:
(1)JSON只允许使用UTF-8编码,不存在编码问题;
(2)JSON内容仅包含key-value键值对,格式简单,不存在冗余结构,是一种轻量级结构;
(3)浏览器内置JSON支持,如果把数据用JSON发送给浏览器,可以用JavaScript直接处理;
所以,开发web应用的时候,使用JSON作为数据传输,在浏览器端非常方便。因为JSON非常适合JavaScript处理,所以,绝大多数REST API都选择JSON作为数据传输格式。
在使用Java进行应用程序的开发中,可能会遇到“将Java对象转换成JSON格式”或者“将JSON格式的数据转换成Java对象”的需求。
常用于解析JSON的第三方库有
- Jackson
- Gson
- Fastjson
三、如何应用JSON
在应用JSON时我们最常用Fastjson这个库,Fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化微JSON字符串,也可以从JSON字符串反序列化到JavaBean。
Fastjson主要使用JSON、jsonObject、jsonArrary三个类。其中JSONArray和JSONObject继承自JSON。
JSON类
JSON类的作用主要是用于原始转换,常用方法有:
将Java对象“序列化“(转换)为JSON字符串。
使用:JSON.toJSONString(Object object),如果需要定义json中的key,可以使用@JSONField注解在成员变量进行设置
//实体数据
PoliceStation ps1=new PoliceStation();
ps1.setName("电子城派出所");
ps1.setAddr("雁塔区电子二路");
ps1.setTel("1234567890");
//序列化:将Java对象转换成json格式的数据
String s1=JSON.toJSONString(ps1);
System.out.println(s1);
将JSON字符串反序列化为Java对象
JSON.parseObject(String text)
//反序列化:将json格式的字符串转换成Java对象
String s2="{\"addr\":\"雁塔区电子二路\",
\"name\":\"电子城派出所\",
\"tel\":\"1234567890\"}\r\n }";
PoliceStation ps2= JSON.parseObject(s2,PoliceStation.class);
System.out.println(ps2);
JSON.parseArray(String text)
// json格式数据
String jsonStr = "[{ \"name\": \"文保分局沪东高校派出所\", \"addr\": \"中山北一路801号\", \"tel\": \"22027732\" }, { \"name\": \"文保分局沪西高校派出所\", \"addr\": \"芙蓉江路55号\", \"tel\": \"62751704\" }, { \"name\": \"水上公安局吴淞水上派出所\", \"addr\": \"淞浦路187号\", \"tel\": \"56671442\" }, { \"name\": \"水上公安局杨浦水上派出所\", \"addr\": \"杨树浦路1291号\", \"tel\": \"65898004\" }, { \"name\": \"水上公安局外滩水上派出所\", \"addr\": \"中山东二路8弄3号\", \"tel\": \"63305388\" }, { \"name\": \"水上公安局石洞口水上派出所\", \"addr\": \"盛石路18号\", \"tel\": \"56152176\" }, { \"name\": \"轨道分局上海火车站站派出所\", \"addr\": \"共和新路2838号\", \"tel\": \"56650472\" }, { \"name\": \"轨道分局徐家汇站派出所\", \"addr\": \"沪闵路6707号\", \"tel\": \"63189188*70246\" }, { \"name\": \"轨道分局人民广场站派出所\", \"addr\": \"南京西路19号\", \"tel\": \"63189188*76369\" }, { \"name\": \"轨道分局陆家嘴站派出所\", \"addr\": \"龙阳路1990号乙\", \"tel\": \"63189188*27132\" }, { \"name\": \"轨道分局宜山路站派出所\", \"addr\": \"零陵路668号\", ... \"崇明县公安局新村派出所\", \"addr\": \"新村乡新中村新跃160号\", \"tel\": \"59650598\" }, { \"name\": \"崇明县公安局新海派出所\", \"addr\": \"新海农场场部北侧\", \"tel\": \"59655712\" }, { \"name\": \"崇明县公安局长征派出所\", \"addr\": \"长征农场派出所生活区长征农场场部\", \"tel\": \"59311459\" }, { \"name\": \"崇明县公安局长江派出所\", \"addr\": \"东风农场林风公路1579号\", \"tel\": \"59641914\" }, { \"name\": \"崇明县公安局东旺派出所\", \"addr\": \"前哨农场前哨公路18号\", \"tel\": \"59471109\" }, { \"name\": \"崇明县公安局东滩湿地保护区治安派出所\", \"addr\": \"陈家镇瀛陈公路崇明县团结沙\", \"tel\": \"59404611\" }, { \"name\": \"崇明县公安局长兴派出所\", \"addr\": \"长兴镇海舸路659号\", \"tel\": \"56851431\" }, { \"name\": \"崇明县公安局横沙派出所\", \"addr\": \"民东路1588号\", \"tel\": \"24060670\" }]";
List<PoliceStation> stationList = JSON.parseArray(jsonStr, PoliceStation.class);
for(PoliceStation ps : stationList) {
System.out.println("警局名称:" + ps.getName());
System.out.println("警局地址:" + ps.getAddr());
System.out.println("联系电话:" + ps.getTel());
}
常见问题
问题1:FastJson默认过滤null值,不显示null字段
Map<String, Object> map = new HashMap<String, Object>(){
{
put("age", 20);
put("name", "灰灰");
put("sex", null);
}
};
System.out.println(JSONObject.toJSONString(map));
//输出结果
{"name:"灰灰","age":20}
解决方法:转换成JSON字符串时,使用Feature枚举值进行设置
Map<String, Object> map = new HashMap<String, Object>(){
{
put("age", 20);
put("name", "灰灰");
put("sex", null);
}
};
// 使用Feature类型的枚举值进行设置
System.out.println(JSONObject.toJSONString(map,Feature.WriteMapNullValue));
问题2:控制JSON的字段顺序
//实体类
public class PoliceStation {
private String name;
private String addr;
private String tel;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
}
//测试类
PoliceStation ps = new PoliceStation();
ps.setName("电子城派出所");
ps.setAddr("雁塔区电子二路343号");
ps.setTel("13324554332");
System.out.println(JSON.toJSONString(ps));
//输出结果
{"addr":"雁塔区电子二路343号","name":"电子城派出所","tel":"13324554332"}
解决方法:输出结果与字段定义顺序不一致,需要在定义实体类字段时,使用@JSONField注解的ordinal属性进行顺序配置
import com.alibaba.fastjson2.annotation.JSONField;
public class PoliceStation {
@JSONField(ordinal = 1)
private String name;
@JSONField(ordinal = 2)
private String addr;
@JSONField(ordinal = 3)
private String tel;
}
问题3:控制JSON的Date字段格式
//实体类
// 订单类
public class Order{
// 订单编号
private String orderId;
// 创建日期
private LocalDateTime creationTime;
public Order() {
this.orderId = UUID.randomUUID().toString();
this.creationTime = LocalDateTime.now();
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public LocalDateTime getCreationTime() {
return creationTime;
}
public void setCreationTime(LocalDateTime creationTime) {
this.creationTime = creationTime;
}
}
//测试类
public class Test {
public static void main(String[] args) {
Order order1 = new Order();
String json = JSON.toJSONString(order1);
System.out.println(json);
}
}
//输出结果
{"creationTime":"2022-07-10 19:39:07.377","orderId":"dbee4f0c-ced7-463f-b19b-c83234cbd5b0"}
解决方法:输出日期字段时,默认格式不符合需求时,可以在定义实体类的Date字段,使用@jsonFie注解的format属性进行格式配置
// 订单类
public class Order{
// 订单编号
private String orderId;
// 创建日期
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime creationTime;
}
////////////////////////////////////////fastjson2.0 ////////////////////////////////////////////////
fastjson2.0 是fastjson的重要升级,目标是为下一个十年提供一个高性能的JSON库,同一套API支持JSON/JSONB两种协议。近期fastjson 再报安全漏洞,直接给我们发送了高危告警,因此升级fastjson迫在眉睫。
FasterXML Jackson是美国FasterXML公司的一款适用于Java的数据处理工具。Jackson-databind是其中的一个具有数据绑定功能的组件。Jackson-databind可以将Java对象转换成json对象,同样也可以将json转换成Java对象。
astjson、jackson 都支持 AutoType 功能,这个功能在序列化的 JSON 字符串中带上类型信息,在反序列化时,不需要传入类型,实现自动类型识别。
Fastjson2
是Fastjson
的升级版,特征:
- 协议支持:支持
JSON
/JSONB
两种协议 - 部分解析:可以使用
JSONPath
进行部分解析获取需要的值 - 语言支持:
Java
/Kotlin
- 场景支持:
Android8+
/服务端 - 其他特性支持:
Graal Native-Image
、JSON Schema
fastjson2 的设计
fastjson2 AutoType 必须显示打开才能使用,没有任何白名单,也不包括任何 Exception 类的白名单。这可以保证缺省配置下是安全的。序列化时带上类型信息,需要使用 JSONWriter.Feature.WriteClassName。比如:
Bean bean = ...;
String jsonString = JSON.toJSONString
(bean, JSONWriter.Feature.WriteClassName);
很多时候,root对象是可以知道类型的,里面的对象字段是基类或者不确定类型,这个时候不输出root对象的类型信息,可以减少序列化结果的大小,也能提升反序列化的性能。
Bean bean = ...;
String jsonString = JSON.toJSONString
(bean, JSONWriter.Feature.WriteClassName,
JSONWriter.Feature.NotWriteRootClassName);
反序列化打开AutoType功能支持自动类型
Bean bean = (Bean) JSON.parseObject
(jsonString, Object.class, JSONReader.Feature.SupportAutoType);
fastjson2 AutoType 支持配置 safeMode,在 safeMode 打开后,显式传入 AutoType 参数也不起作用,具体配置如下:
-Dfastjson2.parser.safeMode=true
fastjson2 AutoType 会经过内置黑名单过滤。该黑名单能拦截大部分常见风险,这个机制不能保证绝对安全,打开 AutoType 不应该在暴露在公网的场景下使用。
序列化示例代码
使用FastJson2JsonRedisSerializer实现RedisSerializer接口
public class FastJson2JsonRedisSerializer<T>implements RedisSerializer<T>{
public static final Charset DEFAULT_CHARSET= Charset.forName("UTF-8");
private Class<T> clazz;
public FastJson2JsonRedisSerializer(Class<T> clazz)
{
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException
{
if (t == null)
{
return new byte[0];
}
return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException
{
if (bytes == null || bytes.length <= 0)
{
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType);
}
}
pom文件中引入fastjson2依赖
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.43</version>
</dependency>
Fastjson Extension
扩展模块
如果项目使用SpringFramework
等框架,可以使用fastjson-extension
模块,使用方式参考 SpringFramework Support。
Maven
:
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension-spring5</artifactId>
<version>2.0.43</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension-spring6</artifactId>
<version>2.0.43</version>
</dependency>