JQuick JSON
JQuick JSON
Section titled “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 最新发布版)。
✨ 核心特性
Section titled “✨ 核心特性”- 完全自研、语法驱动解析:基于自研词法与语法分析组件及 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两组,学习与迁移成本低。
序列化流水线
Section titled “序列化流水线”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 字段不会出现在结果中。
反序列化流水线
Section titled “反序列化流水线”JSON 字符串 → JQuickJSONLexer(词法分析) → JQuickJSONParser(语法分析) → JSONCommonVisitor(Visitor 遍历语法树) → JSONObject / JSONArray / 原生类型 → JSONObject.toBean(clazz):ASM 访问器直接调用 setter 写字段 → 返回目标对象解析错误经 JSONExecutor 的错误监听器输出到标准错误流,deserialize 返回 null 而不抛异常,调用方需对结果判空。
字节码访问器与元数据缓存
Section titled “字节码访问器与元数据缓存”JBeanAccessorFactory 内部维护 ConcurrentHashMap<Class<?>, JBeanAccessor>:
- 首次遇到某类型时收集类层级全部字段(子类在前、父类在后,不含
Object),解析@JSONField/@JSONIgnore元信息; - 类型非接口、非抽象、有公共无参构造器且每个字段都能找到 getter/setter 时,用 jquick-asm 生成
com.github.paohaijiao.mapper.gen.JBeanAccessor_<hash>_<seq>访问器类并加载; - 任一条件不满足或生成失败(
RuntimeException/LinkageError),打印 fallback 日志后回退到JReflectionBeanAccessor; - 访问器与
JBeanFieldMeta元信息按类型永久缓存,后续序列化 / 反序列化零重复解析。
🚀 快速上手
Section titled “🚀 快速上手”环境要求:JDK 8 及以上(JDK 8 / 11 / 17 / 21 均可)。
Maven 与 Gradle 依赖
Section titled “Maven 与 Gradle 依赖”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} }}反序列化为对象
Section titled “反序列化为对象”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}API 与注解详解
Section titled “API 与注解详解”序列化入口工厂
Section titled “序列化入口工厂”JSONSerializerFactory 是获取序列化器的唯一入口:
| 静态方法 | 说明 |
|---|---|
getDefaultSerializer() |
返回全局共享的默认序列化器单例(内部自带空 JContext) |
createJQuickSerializer() |
新建一个默认序列化器实例 |
createJQuickSerializer(JContext context) |
新建带变量上下文的序列化器,用于 ${...} 渲染 |
JSONSerializer 接口
Section titled “JSONSerializer 接口”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 与 JSONArray
Section titled “JSONObject 与 JSONArray”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) 与各类型读取方法。
JSONSupport 工具类
Section titled “JSONSupport 工具类”| 静态方法 | 说明 |
|---|---|
toJsonString(Object) |
通用序列化:POJO 走 fromBean,字符串加引号转义,Map / 集合包装为 JSON 模型 |
prettyPrint(String json, int indentSpaces) |
美化输出;非 JSON 文本原样返回 |
isValidJson(String) |
轻量校验:去空白后以 {} 或 [] 成对包裹即视为通过 |
merge(JSONObject, JSONObject) |
简单合并:后者的键覆盖前者 |
内置注解详解
Section titled “内置注解详解”注解均作用于字段(@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 字段无需标注即自动跳过 |
日期与数字格式化
Section titled “日期与数字格式化”序列化格式化通过 @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 的变量渲染
Section titled “基于 JContext 的变量渲染”模板字段值写成 ${变量名},通过 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 时即携带同一套变量上下文。
JSON 文档合并
Section titled “JSON 文档合并”内置三种合并策略,均返回新对象、不修改入参:
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 与集合
Section titled “枚举、嵌套 Bean 与集合”- 嵌套 Bean:
toBean遇到 JSON 对象且目标字段是非 Map 的 POJO 时递归转换; - 枚举:反序列化时按枚举常量名(
name())匹配转换; - 集合 / 数组:
fromBean自动把Collection与数组包装成JSONArray;deserialize可直接以数组类型或Collection子类接收; - 读取层类型转换:拿到
JSONObject后可用getInteger(key)、getBigDecimal(key)、getDate(key)等按类型取值。
复杂泛型集合的处理方式见常见问题:泛型集合与 TypeReference。
基准性能测试
Section titled “基准性能测试”仓库内置 JMH 风格的基准测试类,可自行运行复测:
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 构建,因此提升相对有限。请以目标环境的实测数据为准。
从 FastJSON 迁移
Section titled “从 FastJSON 迁移”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输出等特性。
从 Jackson 迁移 / Spring Boot 集成
Section titled “从 Jackson 迁移 / Spring Boot 集成”常用 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;
@Configurationpublic class WebJsonConfig {
@Bean public HttpMessageConverter<Object> jquickJsonHttpMessageConverter() { return new JQuickJsonHttpMessageConverter(); }}❓ 常见问题与排错
Section titled “❓ 常见问题与排错”首次序列化某个 POJO 较慢
Section titled “首次序列化某个 POJO 较慢”字节码访问器是懒生成的:每个类型第一次执行 fromBean / toBean 时才扫描字段、生成并加载访问器类,之后永久走缓存。对启动时延敏感的服务,可在启动阶段对热点 DTO 主动调用一次 JBeanAccessorFactory.get(YourDto.class)(或直接序列化一次)完成预热。
什么情况下会回退到反射
Section titled “什么情况下会回退到反射”出现以下任一情况时,该类型自动使用 JReflectionBeanAccessor,功能不受影响但性能为反射水平:类型是接口或抽象类、没有 public 无参构造器、任一字段找不到符合规范的 getter/setter、字节码生成 / 加载抛异常。确认是否回退可查找日志中的 fallback to reflection 关键字。
日期解析失败
Section titled “日期解析失败”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 只做首尾括号的轻量校验,不能替代完整解析。
泛型集合与 TypeReference
Section titled “泛型集合与 TypeReference”由于 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));}空值字段与忽略字段
Section titled “空值字段与忽略字段”fromBean 对值为 null 的字段默认输出 "key":null;不希望输出时在字段上加 @JSONIgnore。static 字段无论是否标注都会被自动忽略;@JSONIgnore 同时作用于序列化与反序列化两个方向。
基于 Apache License 2.0 开源,可商业使用。
- GitHub 仓库:https://github.com/paohaijiao/jquick-json
- Issue 反馈:https://github.com/paohaijiao/jquick-json/issues
- Maven Central:
io.github.paohaijiao:jquick-json:1.5.0 - 版本演进:
1.2.0(解析与生成、JSON 合并、变量上下文、发布 Maven Central)→1.3.0(中文文档)→1.4.0(字节码访问器、反射兜底、元信息缓存)→1.5.0(javelin 2.7.0、jquick-asm 1.3.0)