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");
}

关键点说明:

  1. readObject() 返回下一个字段名,null 表示结束
  2. whatIsNext() 预判下一个值的类型,防止类型错误
  3. skip() 跳过不需要的字段,提升性能
  4. 所有 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,简单粗暴解决问题。


原始标题:Introduction to Jsoniter