[JSON] Fastjson 之版本对比:Fastjson vs Fastjson2

发布时间 2023-11-07 14:41:06作者: 千千寰宇

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> {

}

Fastjson2JSONObject 实现了链结构的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 参考文献