1 Fastjson2
1.1 简述
- FASTJSON相对其他JSON库的特点是快,从2011年fastjson发布1.1.x版本之后,其性能从未被其他Java实现的JSON库超越。
-
Fastjson2相对Fastjson1版本可以说是一次完全重构。
- 这里从代码的角度,分析两者的一些区别;
- 并总结一些新的,让小伙伴们使用Fastjson2或者进行功能扩展的时候,能更加顺畅。
- 除了在性能方面的提升之外,Fastjson 2还解决了一系列安全方面的问题和兼容性的问题,这很大的提高了使用的便捷性。
-
FASTJSON v2是FASTJSON项目的重要升级,目标是为下一个十年提供一个高性能的JSON库。
-
通过同一套API:
- 支持JSON/JSONB两种协议;
- JSONPath 是一等公民;
- 支持全量解析和部分解析;
- 支持Java服务端、客户端Android、大数据场景;支持Kotlin;
- 支持JSON Schema ;
- 支持Graal Native-Image。
1.2 安全漏洞问题
1.2.1 罪魁祸首 AutoType
fastjson、jackson 都支持 AutoType 功能,这个功能在序列化的 JSON 字符串中带上类型信息,在反序列化时,不需要传入类型,实现自动类型识别。
1.2.2 Fastjson V1
fastjson 1.x 内部维护了一个AutoType
的白名单,java 发展近 30 年难免有些漏网之鱼,这也造成近几年 fastjson 安全漏洞频发。
1.2.3 Fastjson V2
- 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 【不应该】在暴露在公网的场景下使用
2 版本对比
2.1 引入依赖
- V1
https://github.com/alibaba/fastjson/releases
https://repo1.maven.org/maven2/com/alibaba/fastjson/
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
截止 2023-11-07 的最新版本为 1.2.83
- V2
https://github.com/alibaba/fastjson2/releases
https://repo1.maven.org/maven2/com/alibaba/fastjson2/fastjson2/
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.42</version>
</dependency>
截止 2023-11-07 的最新版本为 2.0.42
注意:groupId 和v1版本的相比,有所改变。
官方说明:如果原来使用fastjson 1.2.x版本,可以使用兼容包,兼容包不能保证100%兼容,请仔细测试验证,发现问题请及时反馈。兼容包坐标如下:
- 兼容包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.1</version>
</dependency>
2.2 源码对比
2.2.1 整体继承类的修改
在fastjson 2.0中,package和1.x不一样,是com.alibaba.fastjson2。如果你之前用的是fastjson1,大多数情况直接更换包名就即可。
V1
//1.Fastjson 1 JSONObject类定义
public class JSONObject extends JSON implements Map<String,Object> ... {
}
//2.Fastjson 1 JSONArray类定义
public class JSONArray extends JSON implements List<Object> ... {
}
V2
//1.Fastjson2 JSONObject类定义
public class JSONObject extends LinkedHashMap<String, Object> implements InvocationHandler {
}
//2.Fastjson2 JSONArray类定义
public class JSONArray extends ArrayList<Object> {
}
Fastjson2
的JSONObject
实现了链结构的Map
,是有序的Map容器
。
无论是JSONObject
或者JSONArray
都摆脱了JSON
的类,使JSON
由抽象类变成了接口。
package com.alibaba.fastjson2;
class JSON {
// 将字符串解析成JSONObject
static JSONObject parseObject(String str);
// 将字符串解析成JSONArray
static JSONArray parseArray(String str);
// 将字符串解析成Java对象
static T parseObject(byte[] utf8Bytes, Class<T> objectClass);
// 将Java对象输出成字符串
static String toJSONString(Object object);
// 将Java对象输出成UT8编码的byte[]
static byte[] toJSONBytes(Object object);
}
class JSONB {
// 将jsonb格式的byte[]解析成Java对象
static T parseObject(byte[] jsonbBytes, Class<T> objectClass);
// 将Java对象输出成jsonb格式的byte[]
static byte[] toBytes(Object object);
}
class JSONObject {
Object get(String key);
int getIntValue(String key);
Integer getInteger(String key);
long getLongValue(String key);
Long getLong(String key);
T getObject(String key, Class<T> objectClass);
// 将JSONObject对象转换为Java对象
T toJavaObject(Class<T> objectClass);
}
class JSONArray {
Object get(int index);
int getIntValue(int index);
Integer getInteger(int index);
long getLongValue(int index);
Long getLong(int index);
T getObject(int index, Class<T> objectClass);
}
class JSONPath {
// 构造JSONPath
static JSONPath of(String path);
// 根据path直接解析输入,会部分解析优化,不会全部解析
Object extract(JSONReader jsonReader);
// 根据path对对象求值
Object eval(Object rootObject);
}
class JSONReader {
// 构造基于String输入的JSONReader
static JSONReader of(String str);
// 构造基于ut8编码byte数组输入的JSONReader
static JSONReader of(byte[] utf8Bytes);
// 构造基于char[]输入的JSONReader
static JSONReader of(char[] chars);
// 构造基于json格式byte数组输入的JSONReader
static JSONReader ofJSONB(byte[] jsonbBytes)
}
2.2.2 常见类型的优化
- 时间转化类由原来使用SimpleDateFormat转化为 JDK8 提供的
java.time API
,吸收了 joda-time的部分精华,功能更强大,性能也更好。 - 同时,
DateTimeFormatter
是线程安全的。
3 常见使用
3.1 案例:将JSON字符串解析为JSONObject
String text = "...";
JSONObject data = JSON.parseObject(text);
byte[] bytes = ...;
JSONObject data = JSON.parseObject(bytes);
3.2 案例:将JSON字符串解析为JSONArray
String text = "...";
JSONArray data = JSON.parseArray(text);
3.3 案例:将JSON字符串解析为Java对象
String text = "...";
User data = JSON.parseObject(text, User.class);
3.4 案例:将Java对象序列化为JSON字符串
Object data = "...";
String text = JSON.toJSONString(data);
byte[] text = JSON.toJSONBytes(data);
3.5 案例:使用JSONObject、JSONArray
String jsonText = "{\"id\": 2,\"name\": \"fastjson2\"}";
JSONObject obj = JSON.parseObject(jsonText);
int id = obj.getIntValue("id");
String name = obj.getString("name");
String text = "[2, \"fastjson2\"]";
JSONArray array = JSON.parseArray(text);
int id = array.getIntValue(0);
String name = array.getString(1);
X 参考文献
- 阿里Fastjson2强势发布,快看看它有什么改变 - 百度
- Fastjon2他来了,性能显著提升,还能再战十年 - CSDN
- 安全问题我们需要重视,立刻升级fastjson2 - 百度
- maven
- github