1. 概述
JSON(JavaScript Object Notation)作为轻量级的数据交换格式,在现代应用中几乎无处不在。而 Jsoniter 是一款高性能、高灵活性的 Java JSON 解析库,号称比 Jackson、Gson 等主流库更快,同时支持更丰富的运行时控制能力。
本文将带你掌握如何在 Java 项目中使用 Jsoniter 实现高效、灵活的 JSON 解析,尤其适合对性能敏感或需要处理非标准 JSON 结构的场景。
⚠️ 提示:如果你还在用 Jackson 默认配置“一把梭”,遇到类型不匹配就抛异常,那这篇文章能帮你少踩不少坑。
2. 依赖引入
Jsoniter 已发布到 Maven 中央仓库,使用起来非常方便。
Maven 配置
<dependency>
<groupId>com.jsoniter</groupId>
<artifactId>jsoniter</artifactId>
<version>0.9.23</version>
</dependency>
Gradle 配置
implementation 'com.jsoniter:jsoniter:0.9.23'
✅ 推荐使用最新稳定版,可通过 Maven Repository 查询。
3. 使用 Jsoniter 解析 JSON
Jsoniter 提供了三种核心 API 来解析 JSON:
- ✅ Bind API:传统 POJO 绑定,类型安全
- ✅ Any API:动态结构解析,灵活应对不确定性
- ✅ Iterator API:流式手动解析,极致性能控制
下面逐一展开。
3.1 使用 Bind API 进行解析
这是最常见的方式:将 JSON 直接反序列化为 Java 对象。
假设我们有如下 JSON 数据:
{"id":1,"name":{"firstName":"Joe","surname":"Blogg"}}
对应的 Java 类如下:
public class Student {
private int id;
private Name name;
// standard setters and getters
}
public class Name {
private String firstName;
private String surname;
// standard setters and getters
}
使用 Bind API 解析非常简单:
@Test
public void whenParsedUsingBindAPI_thenConvertedToJavaObjectCorrectly() {
String input = "{\"id\":1,\"name\":{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}}";
Student student = JsonIterator.deserialize(input, Student.class);
assertThat(student.getId()).isEqualTo(1);
assertThat(student.getName().getFirstName()).isEqualTo("Joe");
assertThat(student.getName().getSurname()).isEqualTo("Blogg");
}
⚠️ 踩坑提醒:如果 JSON 中 id
字段是字符串 "1"
而不是数字 1
,默认情况下会抛异常。而实际开发中这种“类型不一致”非常常见(比如前端传参习惯用字符串)。
3.2 使用 Maybe 解码器处理模糊类型
当某个字段的类型可能不固定时(如 id
可能是字符串或整数),Jsoniter 提供了 Maybe
系列解码器来优雅处理。
比如我们将 Student.id
改为支持字符串输入:
public class Student {
@JsonProperty(decoder = MaybeStringIntDecoder.class)
private int id;
private Name name;
// standard setters and getters
}
现在即使 JSON 是:
{"id":"1","name":{"firstName":"Joe","surname":"Blogg"}}
也能正常解析:
@Test
public void givenTypeInJsonFuzzy_whenFieldIsMaybeDecoded_thenFieldParsedCorrectly() {
String input = "{\"id\":\"1\",\"name\":{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}}";
Student student = JsonIterator.deserialize(input, Student.class);
assertThat(student.getId()).isEqualTo(1);
}
✅ 其他常用 Maybe 解码器:
MaybeStringLongDecoder
:字符串或长整型MaybeEmptyArrayDecoder
:可能为空数组的字段(避免反序列化失败)
💡 小技巧:这种机制比写自定义
JsonDeserializer
简单粗暴多了,适合快速适配第三方接口。
3.3 使用 Any API 处理动态结构
当 JSON 结构不确定(比如可能返回成功数据或错误信息),Any API 是最佳选择。
比如我们期望返回 Student
数据,但有时会收到错误响应:
{"error":404,"description":"Student record not found"}
如果强行绑定到 Student.class
,必然失败。而用 Any
可以先判断结构再处理:
@Test
public void whenParsedUsingAnyAPI_thenFieldValueCanBeExtractedUsingTheFieldName() {
String input = "{\"id\":1,\"name\":{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}}";
Any any = JsonIterator.deserialize(input);
assertThat(any.toInt("id")).isEqualTo(1);
assertThat(any.toString("name", "firstName")).isEqualTo("Joe");
assertThat(any.toString("name", "surname")).isEqualTo("Blogg");
}
检查字段是否存在
通过 valueType()
判断字段是否存在,避免空指针:
@Test
public void whenParsedUsingAnyAPI_thenFieldValueTypeIsCorrect() {
String input = "{\"id\":1,\"name\":{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}}";
Any any = JsonIterator.deserialize(input);
assertThat(any.get("id").valueType()).isEqualTo(ValueType.NUMBER);
assertThat(any.get("name").valueType()).isEqualTo(ValueType.OBJECT);
assertThat(any.get("error").valueType()).isEqualTo(ValueType.INVALID);
}
ValueType.INVALID
表示字段不存在。
实际应用场景:统一响应处理
String input = "{\"error\":404,\"description\":\"Student record not found\"}";
Any response = JsonIterator.deserialize(input);
if (response.get("error").valueType() != ValueType.INVALID) {
return "Error!! Error code is " + response.toInt("error");
}
return "Success!! Student id is " + response.toInt("id");
输出结果为:Error!! Error code is 4404
✅ 优势:无需定义复杂泛型响应类,逻辑清晰,适合网关、聚合服务等场景。
3.4 使用 Iterator API 手动解析
当你需要极致性能控制或处理超大 JSON 流时,Iterator API 是最优选择。
它采用流式解析,逐字段处理,内存占用低,适合处理 GB 级 JSON 文件或 Kafka 消息。
示例:手动解析 Name
对象
@Test
public void whenParsedUsingIteratorAPI_thenFieldValuesExtractedCorrectly() throws Exception {
Name name = new Name();
String input = "{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}";
JsonIterator iterator = JsonIterator.parse(input);
for (String field = iterator.readObject(); field != null; field = iterator.readObject()) {
switch (field) {
case "firstName":
if (iterator.whatIsNext() == ValueType.STRING) {
name.setFirstName(iterator.readString());
}
continue;
case "surname":
if (iterator.whatIsNext() == ValueType.STRING) {
name.setSurname(iterator.readString());
}
continue;
default:
iterator.skip();
}
}
assertThat(name.getFirstName()).isEqualTo("Joe");
assertThat(name.getSurname()).isEqualTo("Blogg");
}
关键点说明:
readObject()
返回下一个字段名,null
表示结束whatIsNext()
预判下一个值的类型,防止类型错误skip()
跳过不需要的字段,提升性能- 所有
readXxx()
方法必须按顺序调用,符合流式协议
⚠️ 注意:这种方式代码略繁琐,但性能最高,适合中间件、日志解析等底层场景。
4. 总结
Jsoniter 在性能和灵活性上都表现出色,尤其适合以下场景:
- ✅ 接口兼容性差,字段类型不统一(用
Maybe
解码器) - ✅ 响应结构动态(成功/错误混合,用
Any
API) - ✅ 高并发或大数据量解析(用
Iterator
API) - ✅ 替代 Jackson/Gson 做性能优化
API 类型 | 适用场景 | 性能 | 灵活性 |
---|---|---|---|
Bind API | 结构固定、类型明确 | 中 | 低 |
Any API | 结构动态、需运行时判断 | 中高 | 高 |
Iterator API | 超大 JSON、极致性能要求 | 高 | 中 |
项目源码已上传至 GitHub:https://github.com/tech-lead/jsoniter-demo
如果你还在为 JSON 类型转换头疼,不妨试试 Jsoniter,简单粗暴解决问题。