跳转到内容

JQuick JSON

jquick-json 是 JQuick 生态下一套完全自研的轻量级高性能 Java JSON 解析与序列化库。它以哈希映射作为核心数据存储,提供简洁高效的 API,覆盖 JSON 解析、生成、POJO 双向转换、变量渲染与文档合并,自 1.4.0 起 POJO 互转由运行时动态生成的字节码访问器驱动。

库为纯 Java 实现,JDK 8+;JSONObject / JSONArray 底层基于 LinkedHashMap / ArrayList 并实现标准 Map / List 接口,无私有数据模型;解析器由自研语法规则驱动,不绑定 Spring 等任何容器,普通 Java 工程、Spring / Spring Boot 工程均可直接引入。

Maven 坐标:io.github.paohaijiao:jquick-json:1.5.0(Maven Central 最新发布版)。

  • 完全自研、语法驱动解析:基于自研词法与语法分析组件及 Visitor,完整支持对象、数组、字符串、整数、浮点数、布尔、null。
  • 字节码 Bean 访问器:自 1.4.0 起,首次使用某类型时由 jquick-asm 运行时生成访问器类,字段读写编译为直接 getter/setter 调用(INVOKEVIRTUAL),替代逐字段反射。
  • 按类型缓存 + 反射兜底:字节码访问器与字段元信息每个类型只生成 / 解析一次并全局缓存;接口、抽象类、无公共无参构造器或缺 getter/setter 的类型自动回退反射,行为一致。
  • 标准数据模型:JSONObject 实现 Map<String, Object>,JSONArray 实现 List<Object>,底层即 LinkedHashMap / ArrayList,可直接当普通 Map / List 使用。
  • 注解控制字段映射:@JSONField(name, format) 自定义键名与格式化,@JSONIgnore 忽略字段,static 字段自动排除。
  • 变量渲染:序列化时通过 JContext 解析字段值中的 ${...} 占位符,适合模板化 / 配置化 JSON 输出。
  • 多策略 JSON 合并:内置深度合并、浅合并,并支持 JCustomMergeStrategy 自定义合并逻辑。
  • 工具方法齐全:美化打印 prettyPrint、轻量校验 isValidJson、通用序列化 toJsonString、转义工具。
  • 类型覆盖完整:普通 POJO、嵌套 Bean、集合、数组、Date、BigDecimal、各包装类型、枚举按名转换均支持。
  • API 简洁:核心入口仅 serialize / deserialize、fromBean / toBean 两组,学习与迁移成本低。
POJO 对象
→ JBeanAccessorFactory.get(type) // 查缓存:字节码访问器 / 反射访问器
→ 读取 JBeanFieldMeta[] 字段元信息 // name、format、ignored、type 只解析一次
→ ASM 访问器直接调用 getter 读字段值 // INVOKEVIRTUAL,无逐字段反射
→ 按类型分派(Date / BigDecimal / 集合 / 嵌套 Bean …)
→ 装入 JSONObject / JSONArray(LinkedHashMap / ArrayList)
→ toString() 拼接输出 JSON 字符串

字段值为 null 时默认输出 null;标注 @JSONIgnore 的字段与 static 字段不会出现在结果中。

JSON 字符串
→ JQuickJSONLexer(词法分析)
→ JQuickJSONParser(语法分析)
→ JSONCommonVisitor(Visitor 遍历语法树)
→ JSONObject / JSONArray / 原生类型
→ JSONObject.toBean(clazz):ASM 访问器直接调用 setter 写字段
→ 返回目标对象

解析错误经 JSONExecutor 的错误监听器输出到标准错误流,deserialize 返回 null 而不抛异常,调用方需对结果判空。

JBeanAccessorFactory 内部维护 ConcurrentHashMap<Class<?>, JBeanAccessor>:

  1. 首次遇到某类型时收集类层级全部字段(子类在前、父类在后,不含 Object),解析 @JSONField / @JSONIgnore 元信息;
  2. 类型非接口、非抽象、有公共无参构造器且每个字段都能找到 getter/setter 时,用 jquick-asm 生成 com.github.paohaijiao.mapper.gen.JBeanAccessor_<hash>_<seq> 访问器类并加载;
  3. 任一条件不满足或生成失败(RuntimeException / LinkageError),打印 fallback 日志后回退到 JReflectionBeanAccessor;
  4. 访问器与 JBeanFieldMeta 元信息按类型永久缓存,后续序列化 / 反序列化零重复解析。

环境要求:JDK 8 及以上(JDK 8 / 11 / 17 / 21 均可)。

Maven:

<dependency>
<groupId>io.github.paohaijiao</groupId>
<artifactId>jquick-json</artifactId>
<version>1.5.0</version>
</dependency>

Gradle:

implementation 'io.github.paohaijiao:jquick-json:1.5.0'

下面的示例可直接编译运行:

package demo;
import com.github.paohaijiao.factory.JSONSerializerFactory;
import com.github.paohaijiao.serializer.JSONSerializer;
/**
* jquick-json 最简示例
*/
public class JsonQuickStart {
public static class User {
private String name;
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
public static void main(String[] args) {
JSONSerializer serializer = JSONSerializerFactory.getDefaultSerializer();
String json = serializer.serialize(new User("John", 30));
System.out.println(json); // {"name":"John","age":30}
}
}
JSONSerializer serializer = JSONSerializerFactory.getDefaultSerializer();
String json = "{\"name\":\"John\",\"age\":30}";
User user = serializer.deserialize(json, User.class);
System.out.println(user.getName()); // John

目标类型传 JSONObject.class / JSONArray.class 时直接返回哈希模型;传数组类型或 Collection 子类时按数组 / 集合返回:

JSONObject obj = serializer.deserialize("{\"a\":1}", JSONObject.class);
JSONArray arr = JSONArray.parseJSONArray("[1,2,3]");
Object[] array = serializer.deserialize("[1,2,3]", Object[].class);

JSONSupport.prettyPrint 按指定缩进空格数重新排版 JSON 字符串:

String json = serializer.serialize(new User("John", 30));
String pretty = JSONSupport.prettyPrint(json, 3);
System.out.println(pretty);

输出:

{
"name" : "John",
"age" : 30
}

JSONSerializerFactory 是获取序列化器的唯一入口:

静态方法 说明
getDefaultSerializer() 返回全局共享的默认序列化器单例(内部自带空 JContext)
createJQuickSerializer() 新建一个默认序列化器实例
createJQuickSerializer(JContext context) 新建带变量上下文的序列化器,用于 ${...} 渲染
public interface JSONSerializer {
String serialize(Object object);
<T> T deserialize(String json, Class<T> clazz);
}

serialize 接受 POJO、JSONObject / JSONArray、Map、Collection、数组、Number、Boolean;deserialize 的目标类型支持普通 POJO、JSONObject、JSONArray、数组与 Collection 子类。

JSONObject 实现 Map<String, Object>,常用方法:

方法 说明
new JSONObject() / new JSONObject(Map) 创建空对象或由 Map 构造(保留插入顺序)
fromBean(Object) POJO → JSONObject
toBean(Class<T>) JSONObject → POJO
fromMap(Map) / toMap() 与普通 Map 互转(toMap 为深拷贝)
getString / getInteger / getLong / getDouble / getBoolean 按键读取并转换类型,键不存在返回 null
getDate / getBigDecimal / getFloat 按键读取日期 / 高精度数值
deepMergeWith(other) / shallowMergeWith(other) 深度合并 / 浅合并
mergeWith(other, strategy) 按自定义策略合并

JSONArray 实现 List<Object>,额外提供 parseJSONArray(String) 静态解析、toCollection()、getJSONObject(index) / getJSONArray(index) 与各类型读取方法。

静态方法 说明
toJsonString(Object) 通用序列化:POJO 走 fromBean,字符串加引号转义,Map / 集合包装为 JSON 模型
prettyPrint(String json, int indentSpaces) 美化输出;非 JSON 文本原样返回
isValidJson(String) 轻量校验:去空白后以 {} 或 [] 成对包裹即视为通过
merge(JSONObject, JSONObject) 简单合并:后者的键覆盖前者

注解均作用于字段(@Target(ElementType.FIELD),RUNTIME 保留):

import com.github.paohaijiao.anno.JSONField;
import com.github.paohaijiao.anno.JSONIgnore;
import java.util.Date;
public class User {
@JSONField(name = "user_name")
private String name;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date createdAt;
@JSONIgnore
private String internalToken;
// getter / setter 省略
}
注解 属性 说明
@JSONField name 自定义输出 / 读取的 JSON 键名,缺省取 Java 字段名
@JSONField format 格式化 Date、BigDecimal、Float、Double、Integer、Long、Boolean;日期走 SimpleDateFormat,数值走 DecimalFormat
@JSONIgnore — 序列化与反序列化均跳过该字段;static 字段无需标注即自动跳过

序列化格式化通过 @JSONField(format = ...) 声明:

public class Order {
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date orderTime; // 2026-10-01 09:30:00
@JSONField(format = "0.00")
private BigDecimal amount; // 199.00
@JSONField(format = "#,###")
private Integer quantity; // 1,200
}

反序列化 Date 字段时,字符串按以下内置格式依次尝试解析:yyyy-MM-dd、dd/MM/yyyy、MM-dd-yyyy、yyyy MM dd、yyyy.MM.dd、MMM dd, yyyy。

模板字段值写成 ${变量名},通过 JContext 传入变量,再用带上下文的序列化器输出:

import com.github.paohaijiao.param.JContext;
import com.github.paohaijiao.factory.JSONSerializerFactory;
import com.github.paohaijiao.serializer.JSONSerializer;
JUserModel template = new JUserModel("${key}", "${value}");
JContext context = new JContext();
context.put("key", "key1");
context.put("value", "key2");
JSONSerializer serializer = JSONSerializerFactory.createJQuickSerializer(context);
String json = serializer.serialize(template);
System.out.println(json); // {"name":"key1","value":"key2"}

JSONObject 本身也提供 new JSONObject(JContext) 构造器,fromBean 时即携带同一套变量上下文。

内置三种合并策略,均返回新对象、不修改入参:

JSONObject first = serializer.deserialize("{\"a\":1,\"nest\":{\"x\":1}}", JSONObject.class);
JSONObject second = serializer.deserialize("{\"b\":2,\"nest\":{\"y\":2}}", JSONObject.class);
// 浅合并:仅处理顶层键,后者覆盖前者
JSONObject shallow = first.shallowMergeWith(second);
// {"a":1,"nest":{"y":2},"b":2}
// 深度合并:嵌套 JSONObject 递归合并;数组拼接;标量键后者覆盖前者
JSONObject deep = first.deepMergeWith(second);
// {"a":1,"nest":{"x":1,"y":2},"b":2}

需要自定义规则时,用 JCustomMergeStrategy 分别提供对象与数组的合并函数:

import com.github.paohaijiao.merge.impl.JCustomMergeStrategy;
JCustomMergeStrategy strategy = new JCustomMergeStrategy(
(target, source) -> { /* 返回自定义合并后的 JSONObject */ return source; },
(target, source) -> { /* 返回自定义合并后的 JSONArray */ return source; }
);
JSONObject merged = first.mergeWith(second, strategy);
  • 嵌套 Bean:toBean 遇到 JSON 对象且目标字段是非 Map 的 POJO 时递归转换;
  • 枚举:反序列化时按枚举常量名(name())匹配转换;
  • 集合 / 数组:fromBean 自动把 Collection 与数组包装成 JSONArray;deserialize 可直接以数组类型或 Collection 子类接收;
  • 读取层类型转换:拿到 JSONObject 后可用 getInteger(key)、getBigDecimal(key)、getDate(key) 等按类型取值。

复杂泛型集合的处理方式见常见问题:泛型集合与 TypeReference。

仓库内置 JMH 风格的基准测试类,可自行运行复测:

Terminal window
mvn test -Dtest=JBeanAccessorBenchmarkTest

下表为 5 字段 POJO(String / Integer / int / boolean / long)在 JDK 1.8.0_191 / Windows 10 上的官方参考数据(ns/op,纳秒每次操作):

场景 反射 ns/op 字节码 ns/op 加速比
accessor.get(单字段读) 12.2 5.3 2.3x
accessor.set(单字段写) 53.3 1.0 54.0x
accessor.create(实例创建) 2.6 1.4 1.8x
fromBean(bean → JSONObject) 238.8 225.8 1.1x
toBean(JSONObject → bean) 323.4 34.5 9.4x
serialize 完整链路 - 5,780 -
deserialize 完整链路 - 19,277 -

字段写入与 toBean 收益最显著;fromBean 的主要开销在类型分派与 Map 构建,因此提升相对有限。请以目标环境的实测数据为准。

API 命名习惯与 FastJSON 接近,常用调用对照:

FastJSON jquick-json
JSON.toJSONString(obj) serializer.serialize(obj) 或 JSONSupport.toJsonString(obj)
JSON.parseObject(json, User.class) serializer.deserialize(json, User.class)
JSON.parseObject(json) serializer.deserialize(json, JSONObject.class)
JSON.parseArray(json) JSONArray.parseJSONArray(json)
new JSONObject(map) / fluentPut 同名构造 + 标准 Map#put
@JSONField(name = "x") @JSONField(name = "x")(注解同名同义)
@JSONField(format = "...") @JSONField(format = "...")
@JSONField(serialize = false) @JSONIgnore

迁移时需要注意的行为差异:

  • 无全局 SerializerFeature 配置对象:键名、格式化、忽略全部由字段注解决定;
  • 空值字段默认输出 null,不提供全局关闭开关,不需要输出的字段用 @JSONIgnore;
  • JSON 语法错误不抛异常,错误信息输出到 System.err 且 deserialize 返回 null,迁移后注意补判空;
  • 直接 serialize(String) 返回 trim 后的裸文本,字符串字面量请走 JSONSupport.toJsonString;
  • 不提供 FastJSON 的 SerializerFeature.WriteMapNullValue、循环引用检测、$ref 输出等特性。

常用 API 与注解对照:

Jackson jquick-json
objectMapper.writeValueAsString(obj) serializer.serialize(obj)
objectMapper.readValue(json, User.class) serializer.deserialize(json, User.class)
@JsonProperty("x") @JSONField(name = "x")
@JsonIgnore @JSONIgnore
@JsonFormat(pattern = "...") @JSONField(format = "...")

库本身不提供 Spring Boot Starter。需要在 Spring Boot 中把 jquick-json 设为默认 JSON 转换器时,可在业务工程中实现一个基于公共 API 的 HttpMessageConverter(以下为可直接放入项目的业务侧代码,不属于库内置类):

package demo.config;
import com.github.paohaijiao.factory.JSONSerializerFactory;
import com.github.paohaijiao.serializer.JSONSerializer;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 业务侧自定义的 jquick-json 消息转换器
*/
public class JQuickJsonHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
private final JSONSerializer serializer = JSONSerializerFactory.getDefaultSerializer();
public JQuickJsonHttpMessageConverter() {
super(MediaType.APPLICATION_JSON);
}
@Override
protected boolean supports(Class<?> clazz) {
return true;
}
@Override
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
String json = new String(inputMessage.getBody().readAllBytes(), StandardCharsets.UTF_8);
return serializer.deserialize(json, clazz);
}
@Override
protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException {
String json = serializer.serialize(obj);
outputMessage.getBody().write(json.getBytes(StandardCharsets.UTF_8));
}
}

注册为最高优先级的 Bean 即可替换默认的 Jackson 转换器:

package demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
@Configuration
public class WebJsonConfig {
@Bean
public HttpMessageConverter<Object> jquickJsonHttpMessageConverter() {
return new JQuickJsonHttpMessageConverter();
}
}

字节码访问器是懒生成的:每个类型第一次执行 fromBean / toBean 时才扫描字段、生成并加载访问器类,之后永久走缓存。对启动时延敏感的服务,可在启动阶段对热点 DTO 主动调用一次 JBeanAccessorFactory.get(YourDto.class)(或直接序列化一次)完成预热。

出现以下任一情况时,该类型自动使用 JReflectionBeanAccessor,功能不受影响但性能为反射水平:类型是接口或抽象类、没有 public 无参构造器、任一字段找不到符合规范的 getter/setter、字节码生成 / 加载抛异常。确认是否回退可查找日志中的 fallback to reflection 关键字。

deserialize 内置的日期格式固定为 yyyy-MM-dd、dd/MM/yyyy、MM-dd-yyyy、yyyy MM dd、yyyy.MM.dd、MMM dd, yyyy,不在列表内的字符串会抛运行时异常。输出端可在字段上用 @JSONField(format = "yyyy-MM-dd HH:mm:ss") 控制格式;输入端若格式特殊,建议先以 String 接收再自行解析。

非法 JSON 返回 null 而不是抛异常

Section titled “非法 JSON 返回 null 而不是抛异常”

deserialize 把语法错误输出到 System.err(含行列号与规则栈)并返回 null。生产代码务必对返回值判空;JSONSupport.isValidJson 只做首尾括号的轻量校验,不能替代完整解析。

由于 JVM 泛型擦除,deserialize(json, List.class) 只能得到元素为 Object(实际为 JSONObject)的集合。库不提供 TypeReference API,复杂泛型请显式逐项转换:

JSONArray array = JSONArray.parseJSONArray("[{\"name\":\"John\",\"age\":30}]");
List<User> users = new ArrayList<>();
for (Object item : array) {
users.add(new JSONObject((Map<String, Object>) item).toBean(User.class));
}

fromBean 对值为 null 的字段默认输出 "key":null;不希望输出时在字段上加 @JSONIgnore。static 字段无论是否标注都会被自动忽略;@JSONIgnore 同时作用于序列化与反序列化两个方向。

基于 Apache License 2.0 开源,可商业使用。