Không có mô tả

RyanCW 401deb956a Merge pull request #6 from Ryan-CW-Code/next 2 tuần trước cách đây
.vscode f5f0ad5a3c refactor: 持续优化 2 tuần trước cách đây
RyanJson f5f0ad5a3c refactor: 持续优化 2 tuần trước cách đây
example 9354d74652 chore: 各类细节优化 2 tuần trước cách đây
externalModule 8a2226a20d refactor: 持续优化 2 tuần trước cách đây
test f5f0ad5a3c refactor: 持续优化 2 tuần trước cách đây
.clang-format f5f0ad5a3c refactor: 持续优化 2 tuần trước cách đây
.clang-format-ignore 664ddfdad6 feat: beta0 3 tuần trước cách đây
.clang-tidy 664ddfdad6 feat: beta0 3 tuần trước cách đây
.gitignore f5f0ad5a3c refactor: 持续优化 2 tuần trước cách đây
LICENSE 61ec1f4bf1 Initial commit 2 năm trước cách đây
Makefile 742eec54c7 feat: 增加RyanJsonGetArraySize宏 1 năm trước cách đây
README.md f5f0ad5a3c refactor: 持续优化 2 tuần trước cách đây
SConscript ea78d25249 chore(scon): scon依赖变更,适配RT-Thread软件包 1 năm trước cách đây
run_coverage.sh 3bec1d8cac chore: 更新覆盖率测试脚本 2 tuần trước cách đây
xmake.lua f5f0ad5a3c refactor: 持续优化 2 tuần trước cách đây

README.md

RyanJson

希望有兴趣的大佬多试试,找找bug、提提意见

📢 使用过程中遇到问题?欢迎提交 Issue 或在 RT-Thread 社区 提问,感谢支持!

一个针对资源受限的嵌入式设备优化的Json库,内存占用极小的通用Json库,简洁高效!

示例代码请参考example文件夹!

1、介绍

RyanJson 是一个小巧的 C 语言 JSON 解析器,支持 JSON 文本的解析与生成,针对嵌入式平台的 低内存占用 进行了深度优化。

初衷:项目重构后 JSON 结构复杂度提升,cJSON 内存占用过高,无法满足嵌入式场景需求。

✅ 特性亮点

  • 💡 极致内存优化:在资源受限设备上实现 40-70% 内存节省(对比 cJSON),在 RT-Thread 平台 malloc 头部空间为 12 字节时节省约 40%,无 malloc 头部空间可接近 70%。同时保持工业级健壮性,运行速度与 cJSON 基本持平。
  • 🔍 模糊测试保障:基于LLVM Fuzzer ,上亿级数据输入分支覆盖率 99.9%,确保在各种非法输入和极端场景下依旧安全。点击在线查看覆盖率信息
  • 🛡️ 运行时安全分析验证,使用 Sanitizer 系列工具,捕获内存越界、Use-after-free、数据竞争、未定义行为、内存泄漏等问题,提升代码健壮性与安全性
  • 📐高质量代码保障 , 引入 clang-tidyCppcheck 进行静态分析,实现接近语法级的"零缺陷",显著提升可维护性与可读性
  • 🤖 AI 辅助开发与审查,结合 coderabbitaiCopilotGemini Code Assist,在编码与代码审查阶段持续优化代码质量,构建多层安全防线
  • 🧪 9 大类专项测试用例,覆盖广泛场景,全链路内存泄漏检测,强化稳定性与可靠性
  • ⚙️ 低内存占用:动态内存扩展方案,内存空间利用率高。
  • 👩‍💻 开发者友好:轻松集成,类 cJSON API,迁移成本低。
  • 📜 严格但不严苛:符合 RFC 8259 绝大部分JSON标准,支持无限的Json嵌套级别(需注意堆栈空间)、灵活的配置修改项
  • 🔧 可扩展性:允许注释(需调用mini函数清除注释后再解析)、尾随逗号等无效字符(parse时可配置是否允许)等

2、设计

**RyanJson设计时大量借鉴了 jsoncJSON ! **

Json语法是JavaScript对象语法的子集,可通过下面两个连接学习json语法。

JSON规范

Parsing JSON is a Minefield 建议看看

在RyanJson中,结构体表示最小存储单元(键值对),通过单链表组织,结构如下:

// Json 的最基础节点,所有 Json 元素都由该节点表示。
// 结构体中仅包含固定的 next 指针,用于单向链表串联。
// 其余数据(flag、key、stringValue、numberValue、doubleValue 等)均通过动态内存分配管理。
struct RyanJsonNode
{
	struct RyanJsonNode *next; // 单链表节点指针

	/*
	 * 在 next 后紧跟一个字节的 flag,用于描述节点的核心信息:
	 *
	 * 位分布如下:
	 * bit7   bit6   bit5   bit4   bit3   bit2   bit1   bit0
	 * -----------------------------------------------------
	 * 保留   KeyLen KeyLen HasKey NumExt Type2 Type1 Type0
	 *
	 * 各位含义:
	 * - bit0-2 : 节点类型
	 *            000=Unknown, 001=Null, 010=Bool, 011=Number,
	 *            100=String, 101=Array, 110=Object, 111=Reserved
	 *
	 * - bit3   : 扩展位
	 *            Bool 类型:0=false, 1=true
	 *            Number 类型:0=int, 1=double
	 *
	 * - bit4   : 是否包含 Key
	 *            0=无 Key(数组元素)
	 *            1=有 Key(对象成员)
	 *
	 * - bit5-6 : Key 长度字段字节数
	 *            00=1字节 (≤255)
	 *            01=2字节 (≤65535)
	 *            10=3字节 (≤16M)
	 *            11=4字节 (≤UINT32_MAX)
	 *
	 * - bit7   : 保留位(未来可用于压缩标记、特殊类型等)
	 */

	/*
	 * flag 后若节点包含 key / strValue,则跟随一个指针,
	 * 指向存储区:[ keyLen | key | stringValue ]
	 * 其中 keyLen 的大小由 flag 中的长度信息决定(最多 4 字节)。
	 *
	 * 在指针之后,根据节点类型存储具体数据:
	 * - null / bool : 由 flag 表示
	 * - string      : 由上述指针指向
	 * - number      : 根据 flag 决定存储 int(4字节) 或 double(8字节)
	 * - object      : 动态分配空间存储子节点,链表结构如下:
	 *
	 *   {
	 *       "name": "RyanJson",
	 *   next (
	 *       "version": "xxx",
	 *   next (
	 *       "repository": "https://github.com/Ryan-CW-Code/RyanJson",
	 *   next (
	 *       "keywords": ["json", "streamlined", "parser"],
	 *   next (
	 *       "others": { ... }
	 *   )))
	 *   }
	 */

	/*
	 * 设计特点:
	 * - 一个 Json 节点最多 malloc 两次(一次节点本身,一次可选的 key/stringValue),
	 *   对嵌入式系统非常友好,减少 malloc 头部开销, 尽可能的减少内存碎片。
	 *
	 * - key 和 stringValue 必须通过指针管理:
	 *   * 如果直接放在节点里,虽然只需一次 malloc,
	 *     但修改场景会遇到替换/释放困难。
	 *   * 用户可能传递的 Json 对象不是指针,无法直接替换节点,
	 *     要求应用层传递指针会增加侵入性,不符合“应用层无需修改”的目标。
	 *
	 * - 因此采用指针方式,保证灵活性和低侵入性。
	 */
};

typedef struct RyanJsonNode *RyanJson_t;

3、测试体系

LLVM模糊测试(核心亮点),模糊测试是 RyanJson 的 核心稳定性保障

点击在线查看覆盖率信息

  • 千万级测试样本LLVM Fuzzer 自动生成并执行上亿级随机与非法 JSON 输入。
  • 覆盖率极高:分支覆盖率 99.9%,无崩溃、无泄漏。
  • 鲁棒性验证:内存申请失败、扩容失败、非法转义字符、尾随逗号、嵌套过深、随机类型切换。
  • 内存安全验证:结合 Sanitizer 工具链,确保无泄漏、无悬空指针、无越界。
测试类别 测试目标
内存故障测试 验证内存不足时的健壮性
解析 Json 节点测试 随机非法/合法输入解析
循环遍历删除节点测试 确保链表删除安全性
循环遍历分离节点测试 验证节点分离逻辑正确性
Json 压缩去除转义测试 检查转义字符处理健壮性
Json 打印和解析测试 序列化与反序列化一致性
循环遍历获取 Value 测试 确保随机访问稳定性
循环遍历随机复制 Json 节点测试 验证深拷贝与浅拷贝安全性
循环遍历随机修改节点类型/创建节点测试 动态扩展与类型切换能力

📊 手写的专项基础测试用例

测试类别 测试目标
文本解析 Json 测试 基础解析与加载能力验证
创建 Json 节点树测试 节点生成与结构正确性检查
修改 Json 节点测试 字段修改的条件覆盖与正确性
删除 Json 节点测试 各类删除场景与边界条件验证
分离 Json 节点测试 节点分离与引用关系稳定性
替换 Json 节点测试 节点替换行为与一致性验证
比较 Json 节点测试 节点比较与等价性一致性
复制 Json 节点测试 深拷贝/浅拷贝的语义与完整性
Json 循环测试 遍历性能与迭代稳定性

4、代码质量与规范

✅ 工具链全面集成

工具 用途
Sanitizer 运行时捕获内存与线程安全问题
clang-tidy 静态分析潜在缺陷(空指针、资源泄漏等)
Cppcheck 深度扫描内存与资源问题
ClangFormat 统一代码风格
编译器警告 -Wall -Wextra(默认)、-Weffc++/-Weverything(Clang 可选,CI 强化时开启)

✅ 检查重点覆盖

  • 内存安全:杜绝泄漏、越界、悬空指针
  • 性能优化:减少冗余拷贝与低效算法
  • 可读性:命名规范、注释完整、逻辑清晰

成果:实现接近语法级"零缺陷",长期维护成本大幅降低

5、示例

测试代码和示例代码可在本项目根目录RyanJsonExample文件夹查看。

性能测试

请移步文末的 RyanDocs 文档中心查看,是基于 yyjson_benchmark 的测试结果

内存占用测试

*(20251222 linux平台,不考虑malloc头部空间)测试代码可在本项目根目录RyanJsonExample文件夹查看。新版本内存占用更低!*

*****************************************************************************
*************************** RyanJson / cJSON / yyjson 内存对比程序 **************************
*****************************************************************************

--------------------------- 混合类型json数据测试 --------------------------
json原始文本长度为 2265, 序列化后RyanJson内存占用: 3613, cJSON内存占用: 9160, yyjson内存占用: 8692
比cJSON节省: 60.56% 内存占用, 比yyjson节省: 58.43% 内存占用

--------------------------- 对象占多json数据测试 --------------------------
json原始文本长度为 3991, 序列化后RyanJson内存占用: 5436, cJSON内存占用: 11633, yyjson内存占用: 12640
比cJSON节省: 53.27% 内存占用, 比yyjson节省: 56.99% 内存占用

--------------------------- 数组占多json数据测试 --------------------------
json原始文本长度为 1205, 序列化后RyanJson内存占用: 2365, cJSON内存占用: 7340, yyjson内存占用: 5028
比cJSON节省: 67.78% 内存占用, 比yyjson节省: 52.96% 内存占用

--------------------------- 小对象json 混合类型内存占用测试 --------------------------
json原始文本长度为 90, 序列化后RyanJson内存占用: 131, cJSON内存占用: 309, yyjson内存占用: 636
比cJSON节省: 57.61% 内存占用, 比yyjson节省: 79.40% 内存占用

--------------------------- 小对象json 纯字符串内存占用测试 --------------------------
json原始文本长度为 100, 序列化后RyanJson内存占用: 144, cJSON内存占用: 339, yyjson内存占用: 636
比cJSON节省: 57.52% 内存占用, 比yyjson节省: 77.36% 内存占用

RT-Thread平台考虑malloc头部空间12字节情况下,嵌入式平台下占用最高的反而是malloc的内存头开销,所以建议用户优先选择malloc头部空间小的heap管理算法

*****************************************************************************
*************************** RyanJson / cJSON / yyjson 内存对比程序 **************************
*****************************************************************************

--------------------------- 混合类型json数据测试 --------------------------
json原始文本长度为 2265, 序列化后RyanJson内存占用: 7993, cJSON内存占用: 13732, yyjson内存占用: 8752
比cJSON节省: 41.79% 内存占用, 比yyjson节省: 8.67% 内存占用

--------------------------- 对象占多json数据测试 --------------------------
json原始文本长度为 3991, 序列化后RyanJson内存占用: 10668, cJSON内存占用: 19109, yyjson内存占用: 12712
比cJSON节省: 44.17% 内存占用, 比yyjson节省: 16.08% 内存占用

--------------------------- 数组占多json数据测试 --------------------------
json原始文本长度为 1205, 序列化后RyanJson内存占用: 5449, cJSON内存占用: 10424, yyjson内存占用: 5076
比cJSON节省: 47.73% 内存占用, 比yyjson节省: -7.35% 内存占用

--------------------------- 小对象json 混合类型内存占用测试 --------------------------
json原始文本长度为 90, 序列化后RyanJson内存占用: 287, cJSON内存占用: 477, yyjson内存占用: 672
比cJSON节省: 39.83% 内存占用, 比yyjson节省: 57.29% 内存占用

--------------------------- 小对象json 纯字符串内存占用测试 --------------------------
json原始文本长度为 100, 序列化后RyanJson内存占用: 300, cJSON内存占用: 567, yyjson内存占用: 672
比cJSON节省: 47.09% 内存占用, 比yyjson节省: 55.36% 内存占用

RFC 8259 标准测试,大部分嵌入式场景不会出现极为特殊的Unicode字符集

如果项目需要完全兼容Unicode字符集,可以考虑yyjson / json-c

*****************************************************************************
*************************** RyanJson / cJSON / yyjson RFC8259标准测试 **************************
*****************************************************************************
开始 RFC 8259 JSON 测试
--------------------------- RFC8259  RyanJson --------------------------
1 数据不完全一致 -- 原始: {"min":-1.0e+28,"max":1.0e+28} -- 序列化: {"min":-9999999999999999583119736832.0,"max":9999999999999999583119736832.0}
2 数据不完全一致 -- 原始: [123e65] -- 序列化: [12300000.0]
应该失败,但是成功: 123, len: 4
3 数据不完全一致 -- 原始: [-123123e100000] -- 序列化: [-123123.0]
4 数据不完全一致 -- 原始: {"foo\u0000bar":42} -- 序列化: {"foo":42}
5 数据不完全一致 -- 原始: [1E22] -- 序列化: [100.0]
6 数据不完全一致 -- 原始: [1eE2] -- 序列化: [100.0]
应该失败,但是成功: [1eE2], len: 6
应该成功,但是失败: [20e1], len: 6
7 数据不完全一致 -- 原始: [123e45] -- 序列化: [12300000.0]
8 数据不完全一致 -- 原始: ["\u0000"] -- 序列化: [""]
9 数据不完全一致 -- 原始: [123123e100000] -- 序列化: [123123.0]
应该成功,但是失败: [0e1], len: 5
10 数据不完全一致 -- 原始: [123.456e78] -- 序列化: [12345600000.0]
11 数据不完全一致 -- 原始: [-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] -- 序列化: [-1.000000e-78]
12 数据不完全一致 -- 原始: {"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430"} -- 序列化: {"title":"Полтора Землекопа"}
RFC 8259 JSON: (318/322)

--------------------------- RFC8259  cJSON --------------------------
应该失败,但是成功: [1.], len: 4
1 数据不完全一致 -- 原始: {"min":-1.0e+28,"max":1.0e+28} -- 序列化: {"min":-1e+28,"max":1e+28}
2 数据不完全一致 -- 原始: [
                           ] -- 序列化: []
应该失败,但是成功: [
                     ], len: 3
3 数据不完全一致 -- 原始: ["\uqqqq"] -- 序列化: [""]
应该失败,但是成功: ["\uqqqq"], len: 10
应该失败,但是成功: [2.e-3], len: 7
应该失败,但是成功: [-2.], len: 5
应该失败,但是成功: [-.123], len: 7
应该失败,但是成功: 123, len: 4
4 数据不完全一致 -- 原始: [-123123e100000] -- 序列化: [null]
5 数据不完全一致 -- 原始: [-1e+9999] -- 序列化: [null]
6 数据不完全一致 -- 原始: {"foo\u0000bar":42} -- 序列化: {"foo":42}
应该失败,但是成功: [2.e+3], len: 7
应该失败,但是成功: [0.e1], len: 6
7 数据不完全一致 -- 原始:  -- 序列化: [""]
8 数据不完全一致 -- 原始: [0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] -- 序列化: [null]
9 数据不完全一致 -- 原始: ["\u0000"] -- 序列化: [""]
10 数据不完全一致 -- 原始: {} -- 序列化: {}
11 数据不完全一致 -- 原始: ["a -- 序列化: ["a"]
应该失败,但是成功: ["a, len: 7
应该失败,但是成功: [-012], len: 6
12 数据不完全一致 -- 原始: [ -- 序列化: []
应该失败,但是成功: [, len: 3
应该失败,但是成功: [012], len: 5
应该失败,但是成功: ["new
line"], len: 12
13 数据不完全一致 -- 原始: [ -- 序列化: [""]
应该失败,但是成功: ["  "], len: 5
14 数据不完全一致 -- 原始: [1.5e+9999] -- 序列化: [null]
应该失败,但是成功: [-01], len: 5
15 数据不完全一致 -- 原始: [123123e100000] -- 序列化: [null]
16 数据不完全一致 -- 原始: [-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] -- 序列化: [-1e-78]
17 数据不完全一致 -- 原始: {"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430"} -- 序列化: {"title":"Полтора Землекопа"}
应该失败,但是成功: [2.e3], len: 6
RFC 8259 JSON: (305/322)

--------------------------- RFC8259  yyjson --------------------------
1 数据不完全一致 -- 原始: {"min":-1.0e+28,"max":1.0e+28} -- 序列化: {"min":-1e28,"max":1e28}
2 数据不完全一致 -- 原始: [-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] -- 序列化: [-1e-78]
3 数据不完全一致 -- 原始: {"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430"} -- 序列化: {"title":"Полтора Землекопа"}
RFC 8259 JSON: (322/322)
|||----------->>> area = 0, size = 0

6、局限性

  • 使用int / double表示 JSON number 类型,可能存在精度丢失。建议 64 位数值用字符串表示。
  • 对象中允许有重复的key,RyanJson库采用单向链表,链表结构会返回第一个匹配项。

7、文档

📂 示例代码:RyanJsonExample 文件夹

📖 文档中心:RyanDocs

📧 联系方式:1831931681@qq.com