Fastjson2 JSONB 用法
在处理大规模JSON数据时,你是否遇到过序列化性能瓶颈?阿里巴巴Fastjson2带来的JSONB二进制协议,和Fastjson1相比,性能有非常大的提升,体积减少30%~40%。下面介绍Fastjson2 JSONB的使用方法。
一、什么是JSONB?为什么需要它?
1.1 Fastjson2与JSONB简介
Fastjson2是阿里巴巴开源的高性能JSON处理库,是Fastjson 1.x的完全重构版本。与1.x相比,Fastjson2在性能、安全性和功能上都有质的飞跃,同时彻底解决了1.x中长期存在的autoType安全漏洞。
Fastjson2最吸引人的特性之一,是用同一套简洁的API同时支持传统的文本JSON和高效的二进制JSONB。这意味着你不需要为了性能而学习一套全新的API,迁移成本极低。
1.2 JSONB的核心性能优势
JSONB(JSON Binary)是一种二进制编码格式,它将数据从{"name":"张三","age":25}这样的文本字符串,转换为紧凑的字节序列。它带来了三大核心优势:
1. 体积更小、传输更快
文本JSON中的属性名、冒号、引号、括号都会占用字节。JSONB用简短的“类型标记”和“引用ID”替代冗长的字符串,一个包含1000个简单对象的数组,JSONB体积比普通JSON小约30%~40%。
2. 解析速度成倍提升
文本JSON解析需要词法分析、语法分析等步骤;而JSONB是二进制格式,结构信息直接“告诉”了解析器,跳过了大量字符串处理逻辑。
3. 内存效率更高
二进制格式在内存中表示更紧凑,创建的对象更少,有助于减少GC压力,对Android应用或长期运行的Java服务至关重要。
适用场景速览
- 高并发接口: 强烈推荐。JSONB序列化速度更快、数据体积更小,能有效降低接口延迟和网络开销。
- 移动端App:推荐。JSONB体积减少30%~40%,可显著降低移动网络流量消耗。*
- 大数据处理:推荐。批量数据场景下,JSONB能提升解析吞吐量,减少GC压力。*
- 简单配置文件: 普通JSON即可。配置文件通常读取频率低、体积小,无需引入二进制格式。
- 日志记录: 文本JSON更友好。日志需要人工可读、支持grep检索,文本格式更方便排障。
从2.0.48版本开始,官方持续优化JSONB.toBytes和JSONB.parseObject的性能,JSONB在数值处理、集合类型等方面的能力不断增强。
二、JSONB环境搭建(Maven/Gradle)
2.1 Maven依赖配置
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.61</version>
</dependency>
2.2 Gradle依赖配置
dependencies {
implementation 'com.alibaba.fastjson2:fastjson2:2.0.61'
}
2.3 核心导入说明
Fastjson2的包名是com.alibaba.fastjson2,与1.x的com.alibaba.fastjson完全不同。核心类如下:
import com.alibaba.fastjson2.JSON; // 推荐优先使用
import com.alibaba.fastjson2.JSONB; // JSONB专用
import com.alibaba.fastjson2.JSONObject; // 手动解析
import com.alibaba.fastjson2.JSONArray; // JSON数组
注意不要和1.x的包名混用,否则会报编译错误。
三、JSONB核心用法与技巧
3.1 序列化与反序列化
Java对象、 JSONB字节数组互转例子如下:
// 定义User类
@Data
public class User {
private Long id;
private String name;
private Integer age;
private List<String> tags;
}
// 调用方法
// ===== 序列化:Java对象 → JSONB字节数组 =====
User user = new User();
user.setId(1001L);
user.setName("张三");
user.setAge(28);
user.setTags(Arrays.asList("Java", "Fastjson2"));
User user2 = new User();
user2.setId(1002L);
user2.setName("李四");
user2.setAge(30);
user2.setTags(Arrays.asList("PHP", "Laravel"));
// 方式1:基础用法
byte[] jsonbBytes = JSONB.toBytes(user);
// 方式2:带格式化配置
byte[] bytes = JSONB.toBytes(user, JSONWriter.Feature.PrettyFormat);
// ===== 反序列化:JSONB字节数组 → Java对象 =====
// 基于Class
User deserializedUser = JSONB.parseObject(jsonbBytes, User.class);
// ===== 集合类型示例 =====
List<User> users = Arrays.asList(user, user2);
byte[] listBytes = JSONB.toBytes(users);
List<User> parsedList = JSONB.parseArray(listBytes, User.class);
// 基于TypeReference(处理泛型)
List<User> userList2 = JSONB.parseObject(listBytes, new TypeReference<List<User>>() {});
3.2 字符串与JSONB互转
如果你的JSONB数据来自日志分析或跨语言调用,现在有一个极其有用的模式,看下面的例子
private static byte[] loadFromMessageQueue() {
// 实际场景中:从 RocketMQ、Kafka、RabbitMQ 等 MQ 接收的 byte[]
// 这里模拟一个 JSONB 字节数组的构造过程
// 先造一个对象,序列化为 JSONB
JSONObject original = new JSONObject();
original.put("id", 1001);
original.put("name", "张三");
original.put("age", 28);
// 转为 JSONB 字节数组(模拟 MQ 收到的消息体)
return JSONB.toBytes(original);
}
// 调用方法
// 1. 将普通JSON字符串转为JSONB字节数组
String normalJson = "{\"id\":1001,\"name\":\"张三\",\"age\":28}";
byte[] jsonbBytes = JSONB.toBytes(normalJson);
// 2. 再将JSONB转回可读的JSONObject
byte[] jsonbData = loadFromMessageQueue(); // 从MQ接收的二进制数据
JSONObject obj = JSONB.parseObject(jsonbData);
String name = obj.getString("name"); // "张三"
System.out.println(name); // 输出张三
3.3 JSONB序列化特性配置
Fastjson2通过位运算管理特性开关,支持细粒度控制:
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONB;
// 写入特性配置
byte[] bytes = JSONB.toBytes(user,
JSONWriter.Feature.WriteMapNullValue, // 输出null值字段
JSONWriter.Feature.PrettyFormat, // 美化格式
JSONWriter.Feature.WriteClassName // 写入类型信息(安全使用)
);
// 读取特性配置
User user = JSONB.parseObject(bytes, User.class,
JSONReader.Feature.SupportAutoType, // 支持@type(需配合白名单)
JSONReader.Feature.FieldBased // 字段优先访问
);
安全提醒:SupportAutoType默认关闭。如需开启,务必配合setAcceptClassNames设置严格的白名单:
JSONReader.Context context = JSONReader.Context.of();
context.setAcceptClassNames("com.example.model.*", "java.time.*");
User user = JSONB.parseObject(bytes, User.class, context);
更安全的做法:避免使用autoType,改用@JSONType(seeAlso = {...})或在反序列化时传入TypeReference。
说明:官方正在分批开发更安全和更方便使用的API选项,以覆盖更强的复杂对象和第三方商业应用场景。
3.4 Java对象JSONBJSON文本调试
开发调试时,常需要查看JSONB结构或快速验证结果:
// JSON文本转JSONB(用于性能对比或直接生成二进制负载)
byte[] binaryFromString = JSONB.toBytes(jsonString);
// JSONB转JSON文本(输出可读结构,便于调试序列化是否符合预期)
String debugJson = JSONB.toJSONString(jsonbBytes);
System.out.println("JSONB deserialized to JSON: " + debugJson);
// JSONB转Java对象一步到位
User restored = JSONB.parseObject(jsonbBytes, User.class);
四、JSONB与传统JSON对比实战
4.1 直接对比测试
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONB;
// 构造测试数据
List<User> userList = buildLargeUserList(10000);
// === 传统JSON序列化 ===
long startJson = System.currentTimeMillis();
byte[] jsonBytes = JSON.toJSONBytes(userList); // 注意:byte[]用作对比
long jsonTime = System.currentTimeMillis() - startJson;
int jsonSize = jsonBytes.length;
// === JSONB序列化 ===
long startJsonb = System.currentTimeMillis();
byte[] jsonbBytes = JSONB.toBytes(userList);
long jsonbTime = System.currentTimeMillis() - startJsonb;
int jsonbSize = jsonbBytes.length;
// 对比结果
System.out.println("JSON 序列化耗时: " + jsonTime + "ms, 大小: " + jsonSize + "B");
System.out.println("JSONB序列化耗时: " + jsonbTime + "ms, 大小: " + jsonbSize + "B");
System.out.println("体积缩小: " + (jsonSize - jsonbSize) * 100.0 / jsonSize + "%");
4.2 性能优化最佳实践
推荐做法:
- 批量数据处理:JSONB的反序列化速度优势在批量数据下更加明显,推荐处理超过500条记录时使用。
- 高频调用接口:高并发场景下,使用JSONB可显著降低序列化延迟和GC压力。
- 内存缓存场景:JSONB字节数组比字符串更紧凑,适合存入内存缓存如Caffeine/Guava Cache。
不适用或需谨慎场景:
- 需要直接给前端返回JSON文本的API接口 → 保持使用传统JSON。
- 日志文件存储和检索 → 文本格式更适合grep和人工查看。
- 跨语言交换时,确保对方语言有对应的JSONB实现(目前Java生态为主,最好提前评估)。
五、JSONB与Redis集成
将JSONB与Redis结合,是提升缓存系统性能的经典场景。相比于用String存储普通JSON,JSONB能够显著减少网络传输和内存开销:
import com.alibaba.fastjson2.JSONB;
import redis.clients.jedis.Jedis;
public RedisJsonbService {
private Jedis jedis;
// 写入Redis(JSONB格式)
public void set(String key, Object obj) {
byte[] jsonbBytes = JSONB.toBytes(obj);
jedis.set(key.getBytes(), jsonbBytes);
}
// 从Redis读取(JSONB格式)
public <T> T get(String key, Class<T> clazz) {
byte[] jsonbBytes = jedis.get(key.getBytes());
if (jsonbBytes == null) return null;
return JSONB.parseObject(jsonbBytes, clazz);
}
// 批量写入示例
public void batchSet(Map<String, User> userMap) {
for (Map.Entry<String, User> entry : userMap.entrySet()) {
jedis.set(entry.getKey().getBytes(), JSONB.toBytes(entry.getValue()));
}
}
}
Spring Data Redis整合(可选配置):当使用RedisTemplate时,可以创建自定义的RedisSerializer<Object>,内部调用JSONB.toBytes()和JSONB.parseObject(),将默认的JDK或Jackson序列化替换为JSONB实现。
六、常见问题FAQ
1. 泛型反序列化失败怎么办?
// 错误方式:运行时泛型被擦除导致类型丢失
List<User> users = JSONB.parseObject(bytes, List.class);
// 正确方式:使用TypeReference传递完整类型信息
List<User> users = JSONB.parseObject(bytes, new TypeReference<List<User>>() {});
2. Timestamp和日期格式处理
从2.0.48版本开始,对Timestamp类型的JSONB解析报错问题已被修复。对于日期格式,推荐设置全局格式:
// 全局配置
JSON.configDateFormat("yyyy-MM-dd HH:mm:ss");
// 或使用注解
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
3. 精度丢失或解析错误
BigDecimal在小数位较多时必须使用UseBigDecimalForDoubles配置:
// 金融场景强制使用BigDecimal
JSON.config(JSONReader.Feature.UseBigDecimalForDoubles, true);
BigDecimal amount = JSONB.parseObject(bytes, BigDecimal.class);
// BigInteger解析错误通常由数值溢出引起,2.0.48+支持Feature.NonErrorOnNumberOverflow避免报错
JSON.config(JSONReader.Feature.NonErrorOnNumberOverflow, true);
注意:2.0.53_preview曾发现INT64类型解析BigDecimal的bug,实际生产请直接升级到2.0.48+的General Availability版本。
4. 集合类型反序列化报错
确保目标集合类型有public无参构造器或使用ArrayList/HashSet等标准实现。从2.0.48开始,Set类型反序列化问题已大幅改进。
5. 枚举类型序列化错误
检查枚举是否被定义为内部static。
6. Debug疑难排查
使用JSONB.toJSONString(jsonbBytes)将二进制数据转回可读JSON格式,帮助确认序列化是否正确。
七、总结与最佳实践
| 特性 | JSONB表现 |
|---|---|
| 序列化速度 | 比JSON提升2倍以上 |
| 数据体积 | 比JSON减少30%~40% |
| API一致性 | 与JSON.toJSONString语法完全一致 |
| 内存效率 | 创建对象更少,GC压力小 |
总结
- 优先使用
JSONB类:通过JSONB.toBytes()和JSONB.parseObject()完成核心操作,语法与JSON类完全一致,零学习成本 - 批量数据必用JSONB:超过500条记录时性能差异显著
- 开启必要特性:根据场景配置
WriteMapNullValue、PrettyFormat等特性 - 类型安全处理:泛型场景使用
TypeReference,集合场景使用parseArray - 安全底线:非必要不开启
SupportAutoType,开启时必须设置白名单 - 版本升级建议:建议使用2.0.48及以上版本,以获得完整的JSONB功能支持和性能优化
Fastjson2 JSONB为Java开发者提供了同一套熟悉的API,换来2倍以上的解析速度和30%以上的体积缩减。无论你的项目是高并发服务端,还是移动端App,升级到Fastjson2并引入JSONB,都是值得投入的。

