fast_json_cj 是一个用仓颉语言编写的高性能JSON处理库,提供了快速的JSON序列化和反序列化功能。该库专为提升JSON处理效率而设计,支持标准JSON数据类型与仓颉语言原生数据结构的相互转换。
介绍
fast_json_cj是一个用仓颉语言编写的高性能JSON处理库,提供了快速的JSON序列化和反序列化功能。该库专为提升JSON处理效率而设计,支持标准JSON数据类型与仓颉语言原生数据结构的相互转换。
项目特性
- 高性能流式序列化和反序列化,内存占用低
- 通过提供宏注解,来简化仓颉代码中 JSON 序列化和反序列化的开发
- 具有较高的数据兼容性,多余字段或少字段可以自动忽视或者填充默认值,对于字段类型不匹配,可自动转换(如:"20"<->20, "true"<->true等)
- 支持基础数据类型(Int、Float、Bool、String、Option)
- 支持复杂数据结构(Array、ArrayList、HashMap、HashSet、LinkedList、ArrayQueue、ArrayDeque、ArrayStack、TreeMap、ConcurrentHashMap)
- 支持 Any 以及 Any 泛型集合
- 支持自定义对象的序列化和反序列化
性能对比
性能测试结果
本测试对 Cangjie 生态中三款 JSON 库进行反序列化与序列化性能对比测试:
- fast_json_cj:本工程,基于流式解析(JsonStreamReader/Writer)
- simd_json(cangjiejson 经过 simdjson 改造并优化ffi 次数):基于 simdjson 的 FFI,解析阶段使用
parse_json_simd得到 JsonValue 再转为强类型 - cangjiejson:基于 stdx.encoding.json 的 JsonValue,
fromJsonValue/toJsonValue与字符串互转
我们在不同大小的JSON文件上对三个库进行了全面的性能测试对比。测试结果显示,fast_json_cj在反序列化和序列化上性能上展现出卓越的表现。
测试环境与方法
- 数据文件:simple.json(约 110 字节)、medium.json(约 845 字节)、complex.json(约 1673 字节)、twitter.json(约 616 KB)
- 迭代次数:Simple 10000 次、Medium 5000 次、Complex 2000 次、Twitter 100 次
- 指标:单次操作平均耗时(μs)、吞吐量(MB/s)
- 预热:每轮测试前各进行 10 次反序列化与 10 次序列化预热
综合性能对比表
下表为三库在四类场景下的平均耗时(μs)与吞吐量(MB/s),以及 fast_json_cj 相对 simd_json / cangjiejson 的提升比例)。
| 场景 | 操作 | fast_json_cj 耗时(μs) | fast_json_cj 吞吐(MB/s) | simd_json 耗时(μs) | simd_json 吞吐(MB/s) | cangjiejson 耗时(μs) | cangjiejson 吞吐(MB/s) | fast_json_cj vs simd_json | fast_json_cj vs cangjiejson |
|---|---|---|---|---|---|---|---|---|---|
| Simple (110B) | 反序列化 | 1 | 104.90 | 2 | 52.45 | 2 | 52.45 | 快 2.0 倍 / 吞吐 2.0 倍 | 快 2.0 倍 / 吞吐 2.0 倍 |
| Simple | 序列化 | 2 | 41.00 | / | / | 3 | 27.33 | / | 快 1.5 倍 / 吞吐 1.5 倍 |
| Medium (845B) | 反序列化 | 6 | 134.30 | 11 | 73.25 | 11 | 73.25 | 快 1.83 倍 / 吞吐 1.83 倍 | 快 1.83 倍 / 吞吐 1.83 倍 |
| Medium | 序列化 | 6 | 84.87 | / | / | 15 | 33.95 | / | 快 2.5 倍 / 吞吐 2.5 倍 |
| Complex (1.6KB) | 反序列化 | 20 | 79.77 | 39 | 40.91 | 40 | 39.88 | 快 1.95 倍 / 吞吐 1.95 倍 | 快 2.0 倍 / 吞吐 2.0 倍 |
| Complex | 序列化 | 18 | 65.75 | / | / | 34 | 34.92 | 快 1.89 倍 / 吞吐 1.88 倍 | |
| Twitter (616KB) | 反序列化 | 5044 | 119.40 | 7138 | 84.37 | 7585 | 79.40 | 快 1.41 倍 / 吞吐 1.42 倍 | 快 1.50 倍 / 吞吐 1.50 倍 |
| 序列化 | 4262 | 106.89 | / | / | 6696 | 68.11 | / | 快 1.57 倍 / 吞吐 1.57 倍 |
性能优势详细分析
反序列化(Parse)
- fast_json_cj 在四种数据规模下反序列化均领先:Simple/Medium 约 2 倍于 simd_json、cangjiejson;Complex 约 2 倍;Twitter 约 1.4~1.5 倍。流式解析、无中间 JsonValue 和强类型直出,是其主要优势。
- simd_json 虽底层使用 simdjson,但需先解析为 JsonValue 再
fromJsonValue转强类型,多一层转换与分配,反序列化耗时和吞吐均明显落后于 fastjson,略优于或持平 cjjson。 - cangjiejson 基于 stdx JsonValue 的通用路径,反序列化在三者中整体最慢,与实现定位(兼容性、易用性优先)一致。
序列化(Stringify)
- fast_json_cj 在 Simple、Complex、Twitter 的序列化上整体最优(耗时更短、吞吐更高)
- cangjiejson 序列化最慢(约 34~68 MB/s),与反序列化结论一致。
项目架构
fast_json_cj 采用模块化设计,主要包括宏定义注解、JSON序列化、反序列化以及流式处理等模块。
源码目录
.
├── README.md # 整体介绍
├── cjpm.toml # 项目配置文件
├── src # 源码目录
│ ├── JsonUtil.cj # JSON工具类
│ ├── exceptions.cj # 异常定义
│ ├── jsonAdapter.cj # JSON适配器接口
│ ├── macros/ # 宏定义目录
│ ├── stream/ # 流式处理模块
│ │ ├── json_serializable.cj # 序列化接口及实现
│ │ ├── json_deserializable.cj # 反序列化接口及实现
│ │ ├── json_reader.cj # JSON读取器
│ │ ├── json_writer.cj # JSON写入器
│ │ └── ... # 其他辅助文件
│ └── test/ # 测试代码目录
└── target/ # 编译输出目录
接口说明
通过提供以下宏,来简化仓颉代码中 JSON 序列化和反序列化的开发:
- 提供
@JsonAdapter宏用于修饰 class/struct,为其自动生成序列化和反序列化的成员方法; - 提供
@JsonName["newName"]宏用于修饰 class/struct 里的成员,使其在序列化和反序列化过程中使用别名; - 提供
@JsonIgnore宏用于修饰 class/struct 里的成员,使其不参与序列化和反序列化; - 提供
@JsonIgnoreNull宏用于修饰 class/struct 里的成员,如果该成员值为 None,则不进行序列化;反序列化仍然正常执行。 - 提供
@JsonDefault宏用于修饰 class/struct 里的成员,在反序列化的时候,如果json字符串中不存在该字段,则保持默认值。 同时对外暴露了如下接口: IStreamJsonAdapter<T>接口:序列化和反序列化的接口,使用@JsonAdapter修饰的 class/struct 默认都实现了该接口;JsonTypeMismatch异常类:在反序列化时,如果JSON的数据类型与 class/struct 成员的类型不一致,会抛出该异常;JsonFieldNotExist异常类:在反序列化时,如果 class/struct 的成员变量名字,在 JSON 字符串中找不到对应的 key,会抛出该异常。JsonGetValueException异常类,调用AnyType的getValue<T>()方法时,如果类型不匹配,会抛出该异常。JsonStreamReader类 :反序列化流解析器。JsonStreamWriter类 :序列化流解析器。
主要注解和接口说明详见 API
使用说明
配置并更新依赖
方式1:git 源码依赖
在您工程的 cjpm.toml 文件中,新增如下源码依赖配置:
[dependencies]
[dependencies.cjjson]
git = "https://gitcode.com/Cangjie-TPC/cjfast_json.git"
branch = "dev"
方式2:本地源码依赖
如果不希望通过 git 依赖本仓,您也可以直接下载本仓库的全量源码(包括本仓库的 cjpm.toml 配置文件),然后在您工程的 cjpm.toml 文件中添加本地模块依赖。
假如您将本仓库下载到您工程的 cjpm.toml 的上一层目录中,并且目录名字为 cjfast_json cjpm.toml 文件中,新增如下配置即可:
[dependencies]
[dependencies.cjfast_json]
path = "../cjfast_json" # 相对路径、绝对路径均可,建议使用相对路径。
version = "1.0.0"
2导入依赖包
在源码中导入以下 package:
import fastjson.* // 所有对外接口,以及宏展开依赖的的接口
import fastjson.macros.* // 宏定义
import fastjson.stream.*
功能示例
例如使用 @JsonAdapter 修饰对应的 class 即可。
@JsonAdapter
public class Demo {
public var param1: Int64 = 0
public var param2: String = ""
public var param3: ?String = None
public var param4: Array<Int64> = Array<Int64>()
public var param5: ArrayList<String> = ArrayList<String>()
public var param6: HashMap<String, Int64> = HashMap<String, Int64>()
}
然后便可直接调用这个类的相关方法完成与 JSON 字符串的互相转换:
let input: String = #"
{
"param1": 123,
"param2": "bbbbb",
"param3": null,
"param4": [1,2,3],
"param5": ["s1","s2","s3"],
"param6": {
"f1":123,
"f2":456
}
}"#
// 方式 1 简便 api
let obj = Demo.fromJson(input) // JSON 字符串反序列化为 Demo 对象
let jsonString: String = obj.toJson() // Demo 对象序列化为 JSON 字符串
// 方式 2 手动流式
let stream = ByteBuffer()
var writer = JsonStreamWriter(stream)
// writer.WriteConfig = WriteConfig.pretty //配置json
var jsonString2 = Demo.toJson(writer)// 序列化
writer.flush()
var jsonString2 = String.fromUtf8(readToEnd(stream))
var demo:Demo = Demo.fromJson(JsonStreamReader(ByteBuffer(jsonString2.toArray()))) // 反序列化
约束与限制
- 目前支持修饰 class、struct 类型,暂不支持 enum 类型。
- @JsonAdapter修饰的类或结构体,必须要有一个无参的构造函数。
开源协议
本项目基于 Apache License ,请自由地享受和参与开源。
参与贡献
欢迎给我们提交PR,欢迎给我们提交Issue,欢迎参与任何形式的贡献。