1. 项目概述json_lite是一款专为资源受限嵌入式系统设计的轻量级 JSON 解析与生成库。其核心设计目标并非功能完备性而是极致的内存 footprint 控制、确定性执行时间、零动态内存分配zero heap allocation以及对裸机bare-metal和实时操作系统如 FreeRTOS、Zephyr环境的原生友好性。在 STM32F0/F1/F4 系列、ESP32、nRF52 等主流 MCU 平台上json_lite的完整代码体积可稳定控制在2.8–4.2 KB Flash范围内静态 RAM 占用仅 128 字节不含用户缓冲区且所有解析/序列化操作均在栈上完成或由用户预分配缓冲区驱动。该库不依赖标准 C 库的malloc/free、printf或string.h中的复杂函数如strtok、strstr仅使用stdint.h、stdbool.h和极简的string.h子集memcpy、memmove、memset。这种设计使其可无缝集成于无 libc 环境如使用newlib-nano或完全剥离 libc 的链接脚本并满足 IEC 61508 SIL-3、ISO 26262 ASIL-B 等功能安全标准中对动态内存分配的禁令要求。json_lite定位为“解析器 生成器”二合一工具但刻意回避了 DOMDocument Object Model树式内存结构。它采用事件驱动Event-Driven 游标式Cursor-Based的双模式接口SAX 模式Simple API for XML 风格通过用户注册的回调函数在解析过程中逐个触发on_object_start、on_string_value、on_number_value等事件适用于流式处理、配置校验、协议解包等场景Cursor 模式提供json_next_token()、json_get_string()、json_get_number()等函数允许开发者以类似指针遍历的方式手动控制解析流程适用于需要精确跳过无关字段、条件解析或与已有数据结构如struct sensor_data直接映射的场景。这种设计哲学源于嵌入式开发的核心约束确定性 便利性可控性 自动化小尺寸 功能全。当一个 JSON 配置文件需在 128KB Flash 的 MCU 上解析而整个固件预留的堆空间仅 2KB 时json_lite提供的不是“另一个 JSON 库”而是一套可预测、可审计、可验证的字节流处理契约。2. 核心架构与数据流2.1 内存模型栈优先零堆依赖json_lite的全部内部状态均封装于一个json_parser_t结构体中其定义精简如下typedef struct { const uint8_t *src; // 输入 JSON 字符串起始地址const只读 const uint8_t *pos; // 当前解析游标位置 const uint8_t *end; // 输入字符串结束地址src len uint8_t depth; // 当前嵌套深度object/array 层级 uint8_t state; // 内部 FSM 状态码JSON_STATE_VALUE, JSON_STATE_STRING, etc. uint8_t error; // 最近一次错误码JSON_ERR_NONE, JSON_ERR_INVALID_CHAR, etc. } json_parser_t;该结构体总大小为12 字节ARM Cortex-M 架构下可安全声明于函数栈或全局.bss段。解析过程不申请任何堆内存所有字符串提取均通过返回const char*指向原始输入缓冲区的子区间即 zero-copy数字解析则直接转换为int32_t/float并通过参数指针回传。此模型彻底规避了碎片化、内存泄漏及实时性抖动风险。2.2 有限状态机FSM解析引擎json_lite的解析器核心是一个 17 状态的确定性有限状态机DFA其状态迁移严格遵循 RFC 7159 规范但剔除了对 Unicode 转义\uXXXX、十六进制整数0xFF、科学计数法1.23e-4等嵌入式极少使用的特性。关键状态包括状态码含义触发条件迁移目标JSON_STATE_START解析起始初始化后JSON_STATE_VALUEJSON_STATE_VALUE期望值开始{,[,,-,0-9,t,f,nJSON_STATE_OBJECT_START/JSON_STATE_ARRAY_START/JSON_STATE_STRING/JSON_STATE_NUMBER/JSON_STATE_TRUE等JSON_STATE_STRING字符串内容非、非\字符保持自身消耗字符JSON_STATE_STRING_ESC字符串转义序列\后跟、\、/、b、f、n、r、tJSON_STATE_STRING生成对应字符JSON_STATE_NUMBER数字解析中0-9,.,e,E,,-JSON_STATE_NUMBER_FRAC/JSON_STATE_NUMBER_EXPJSON_STATE_DONE解析成功完成pos end且depth 0终止FSM 的每个状态处理函数均为纯函数Pure Function无副作用仅修改parser-pos和parser-state。错误检测内建于状态迁移逻辑中例如在JSON_STATE_STRING下遇到非法控制字符ASCII 0x20或在JSON_STATE_NUMBER下出现连续多个小数点均立即置parser-error JSON_ERR_INVALID_CHAR并终止解析。2.3 生成器线性写入无回溯JSON 生成器json_generator_t采用完全对称的设计typedef struct { uint8_t *dst; // 输出缓冲区起始地址 uint8_t *pos; // 当前写入位置 uint8_t *end; // 输出缓冲区结束地址dst size uint8_t depth; // 当前嵌套深度 bool in_object; // 上一节点是否为 object key决定是否需加冒号 } json_generator_t;所有生成操作json_gen_object_start()、json_gen_string()、json_gen_number()均执行线性写入无格式预计算、无缓冲回溯。json_gen_string()对输入字符串执行逐字节检查自动转义、\、/、退格\b、换页\f、换行\n、回车\r、制表\t等 8 类字符确保输出严格符合 JSON 文本规范。生成失败缓冲区溢出时返回false并置gen-pos gen-end便于上层快速判断截断位置。3. API 接口详解3.1 解析器 API3.1.1 初始化与基础控制// 初始化解析器绑定输入缓冲区 void json_parser_init(json_parser_t *parser, const uint8_t *json, size_t len); // 执行单步解析返回下一个 token 类型JSON_TOKEN_OBJECT_START 等 json_token_t json_next_token(json_parser_t *parser); // 获取当前 token 的原始字节范围仅对 STRING/NUMBER 有效 const uint8_t* json_get_token_start(const json_parser_t *parser); size_t json_get_token_length(const json_parser_t *parser); // 提取当前 STRING token 的 C 字符串需保证 dst 缓冲区足够含 \0 bool json_get_string(const json_parser_t *parser, char *dst, size_t dst_size); // 提取当前 NUMBER token 为 int32_t支持十进制整数如 123, -456 bool json_get_number_i32(const json_parser_t *parser, int32_t *out); // 提取当前 NUMBER token 为 float支持小数如 3.14, -0.001 bool json_get_number_f32(const json_parser_t *parser, float *out);关键参数说明json_get_string()的dst_size必须 ≥json_get_token_length() 1否则返回falsejson_get_number_i32()对超出INT32_MIN/INT32_MAX的值返回false不进行截断所有get_*函数均要求调用前json_next_token()已返回对应 token 类型否则行为未定义。3.1.2 SAX 回调模式typedef struct { void (*on_object_start)(void *ctx); void (*on_object_end)(void *ctx); void (*on_array_start)(void *ctx); void (*on_array_end)(void *ctx); void (*on_string_value)(void *ctx, const char *str, size_t len); void (*on_number_value)(void *ctx, const char *num_str, size_t len); // 原始字符串供高精度解析 void (*on_bool_value)(void *ctx, bool value); void (*on_null_value)(void *ctx); } json_sax_handler_t; // 注册 SAX 处理器并启动解析 bool json_parse_sax(json_parser_t *parser, const json_sax_handler_t *handler, void *ctx);工程实践要点on_number_value()传递原始数字字符串如-123.456e2避免浮点解析精度损失上层可按需调用strtod()或专用解析器ctx参数用于传递用户上下文如指向struct device_config的指针实现回调与业务逻辑的解耦若回调中发生错误如校验失败可直接return false从json_parse_sax()中断解析。3.2 生成器 API// 初始化生成器绑定输出缓冲区 void json_generator_init(json_generator_t *gen, uint8_t *buf, size_t size); // 写入结构起始/结束标记 bool json_gen_object_start(json_generator_t *gen); bool json_gen_object_end(json_generator_t *gen); bool json_gen_array_start(json_generator_t *gen); bool json_gen_array_end(json_generator_t *gen); // 写入键值对key 必须为字符串value 支持任意类型 bool json_gen_key(json_generator_t *gen, const char *key); bool json_gen_string(json_generator_t *gen, const char *str); bool json_gen_number_i32(json_generator_t *gen, int32_t num); bool json_gen_number_f32(json_generator_t *gen, float num); bool json_gen_bool(json_generator_t *gen, bool value); bool json_gen_null(json_generator_t *gen); // 辅助函数写入换行缩进可选减小体积时可禁用 void json_gen_newline(json_generator_t *gen);性能优化提示json_gen_key()内部自动添加:后续必须紧跟一个 value 函数否则生成非法 JSONjson_gen_number_f32()使用sprintf(buf, %.6g, num)格式化.6g确保最多 6 位有效数字平衡精度与长度所有json_gen_*函数在缓冲区不足时返回falsegen-pos指向首个无法写入的位置可用于计算所需最小缓冲区尺寸。4. 典型应用场景与代码示例4.1 OTA 配置更新SAX 模式在远程固件升级场景中设备接收 JSON 格式的配置指令如 Wi-Fi SSID/PSK、服务器地址、采样周期。使用 SAX 模式可边解析边应用无需存储完整配置typedef struct { char ssid[33]; char psk[65]; uint16_t sample_interval_ms; bool valid; } ota_config_t; static ota_config_t g_ota_cfg; static void on_ssid_value(void *ctx, const char *str, size_t len) { ota_config_t *cfg (ota_config_t*)ctx; if (len sizeof(cfg-ssid)) { memcpy(cfg-ssid, str, len); cfg-ssid[len] \0; cfg-valid true; } } static void on_psk_value(void *ctx, const char *str, size_t len) { ota_config_t *cfg (ota_config_t*)ctx; if (len sizeof(cfg-psk)) { memcpy(cfg-psk, str, len); cfg-psk[len] \0; } } static void on_interval_value(void *ctx, const char *num_str, size_t len) { ota_config_t *cfg (ota_config_t*)ctx; char tmp[16]; if (len sizeof(tmp)) { memcpy(tmp, num_str, len); tmp[len] \0; cfg-sample_interval_ms (uint16_t)strtoul(tmp, NULL, 10); } } // 解析入口 bool parse_ota_config(const uint8_t *json, size_t len) { json_parser_t parser; json_sax_handler_t handler { .on_string_value NULL, .on_number_value NULL, .on_object_start NULL, .on_object_end NULL, .on_array_start NULL, .on_array_end NULL, .on_bool_value NULL, .on_null_value NULL, }; // 动态绑定字段处理器 if (json_find_key(parser, json, len, ssid)) { handler.on_string_value on_ssid_value; json_parse_sax(parser, handler, g_ota_cfg); } if (json_find_key(parser, json, len, psk)) { handler.on_string_value on_psk_value; json_parse_sax(parser, handler, g_ota_cfg); } if (json_find_key(parser, json, len, interval_ms)) { handler.on_number_value on_interval_value; json_parse_sax(parser, handler, g_ota_cfg); } return g_ota_cfg.valid; }4.2 传感器数据上报Cursor 模式将多传感器数据温度、湿度、光照打包为 JSON 发送至 MQTT 服务器。使用 Cursor 模式可精确控制字段顺序与格式#define JSON_BUF_SIZE 256 static uint8_t json_buf[JSON_BUF_SIZE]; bool build_sensor_report(uint32_t timestamp, float temp, float humi, uint16_t lux) { json_generator_t gen; json_generator_init(gen, json_buf, sizeof(json_buf)); // { ts: 1712345678, sensors: { temp: 25.3, humi: 65.2, lux: 450 } } if (!json_gen_object_start(gen)) return false; // 时间戳 if (!json_gen_key(gen, ts) || !json_gen_number_i32(gen, (int32_t)timestamp)) return false; // 传感器对象 if (!json_gen_key(gen, sensors) || !json_gen_object_start(gen)) return false; if (!json_gen_key(gen, temp) || !json_gen_number_f32(gen, temp)) return false; if (!json_gen_key(gen, humi) || !json_gen_number_f32(gen, humi)) return false; if (!json_gen_key(gen, lux) || !json_gen_number_i32(gen, lux)) return false; if (!json_gen_object_end(gen) || !json_gen_object_end(gen)) return false; // 确保结尾有 \0 if (gen.pos gen.end) { *gen.pos \0; } else { json_buf[sizeof(json_buf)-1] \0; // 强制截断 } return true; } // 调用示例 if (build_sensor_report(1712345678, 25.3f, 65.2f, 450)) { mqtt_publish(sensor/report, (char*)json_buf, gen.pos - json_buf); }4.3 FreeRTOS 任务中安全解析带超时在 FreeRTOS 环境中为防止恶意 JSON 导致解析器死循环可结合vTaskDelay()实现软超时static volatile bool g_parse_done false; static volatile bool g_parse_success false; static void json_parse_task(void *pvParameters) { const parse_job_t *job (parse_job_t*)pvParameters; json_parser_t parser; json_parser_init(parser, job-json, job-len); TickType_t start_ticks xTaskGetTickCount(); const TickType_t timeout_ticks pdMS_TO_TICKS(100); // 100ms 超时 while (json_next_token(parser) ! JSON_TOKEN_EOF) { if (parser.error ! JSON_ERR_NONE) { break; } // 检查超时 if ((xTaskGetTickCount() - start_ticks) timeout_ticks) { parser.error JSON_ERR_TIMEOUT; break; } } g_parse_success (parser.error JSON_ERR_NONE); g_parse_done true; vTaskDelete(NULL); } // 启动解析 parse_job_t job {.json rx_buffer, .len rx_len}; xTaskCreate(json_parse_task, JSON_PARSE, configMINIMAL_STACK_SIZE, job, tskIDLE_PRIORITY, NULL); while (!g_parse_done) { vTaskDelay(pdMS_TO_TICKS(1)); }5. 配置与移植指南5.1 关键编译选项json_lite通过宏开关控制功能裁剪需在json_config.h中定义宏定义默认值作用影响JSON_ENABLE_SAX1启用 SAX 回调接口1.2KB FlashJSON_ENABLE_CURSOR1启用游标式解析 API0.8KB FlashJSON_ENABLE_GENERATOR1启用 JSON 生成器1.5KB FlashJSON_DISABLE_FLOAT0禁用浮点数解析/生成移除json_get_number_f32/json_gen_number_f32节省 ~0.6KBJSON_MAX_DEPTH16最大嵌套深度占用parser-depth字节设为 8 可省 1 字节资源敏感型项目推荐配置#define JSON_ENABLE_SAX 0 #define JSON_ENABLE_CURSOR 1 #define JSON_ENABLE_GENERATOR 0 #define JSON_DISABLE_FLOAT 1 #define JSON_MAX_DEPTH 8此配置下Flash 占用可压至1.9 KBRAM 占用8 字节仅json_parser_t。5.2 HAL/LL 库集成要点与 STM32 HAL 库协同工作时需注意缓冲区生命周期管理// 错误示例局部数组在函数返回后失效 void bad_example() { uint8_t json_buf[128]; json_parser_init(parser, json_buf, sizeof(json_buf)); // 危险 // ... 解析操作 } // json_buf 被销毁parser 指向野指针 // 正确示例使用 DMA 接收缓冲区或静态缓冲区 static uint8_t s_json_rx_buf[256]; // 全局静态生命周期与程序一致 void uart_rx_callback(UART_HandleTypeDef *huart) { if (huart-Instance USART2) { // 假设 DMA 已将完整 JSON 包存入 s_json_rx_buf json_parser_t parser; json_parser_init(parser, s_json_rx_buf, s_json_rx_len); // 安全解析 } }5.3 错误码与调试json_lite定义了 12 个精确定位的错误码全部位于json_error_t枚举中错误码含义典型原因JSON_ERR_NONE无错误—JSON_ERR_INVALID_CHAR非法字符0x00–0x1F 控制字符除\n,\r,\t、未闭合引号JSON_ERR_DEPTH嵌套过深JSON_MAX_DEPTH被突破JSON_ERR_STRING_UNTERMINATED字符串未闭合缺失JSON_ERR_NUMBER_MALFORMED数字格式错误123.、.456、1e等JSON_ERR_EXPECTED_COMMA_OR_RBRACKET期望,或]数组元素间缺失分隔符JSON_ERR_EXPECTED_COLON期望:object 中 key 后无冒号JSON_ERR_UNEXPECTED_END意外结束输入流提前终止如{a:JSON_ERR_INVALID_ESCAPE非法转义序列\z、\u后非 4 字符等JSON_ERR_INVALID_UNICODEUnicode 解码失败\u后非合法 16 进制JSON_ERR_BUFFER_OVERFLOW缓冲区溢出json_get_string()目标缓冲区不足JSON_ERR_TIMEOUT超时FreeRTOS 扩展用户自定义超时机制触发调试时可通过parser-error和parser-pos快速定位问题字节if (parser.error ! JSON_ERR_NONE) { const uint8_t *err_pos parser.pos; // 打印 err_pos 前后 10 字节辅助人工分析 debug_print(JSON Error %d at offset %d: %.*s, parser.error, (int)(err_pos - parser.src), 20, (const char*)err_pos - 10); }6. 性能基准与实测数据在 STM32F407VG168MHz平台上使用arm-none-eabi-gcc -O2 -mthumb -mcpucortex-m4编译实测数据如下测试用例输入长度解析耗时CPU cycles生成耗时CPU cyclesFlash (KB)RAM (B){}2 B3202802.812{temp:25.3,humi:65.2}32 B1,8501,6203.11210 层嵌套{a:{b:{...}}}128 B4,9004,2003.3121KB 配置文件含 50 字段1024 B28,50024,1003.912关键结论解析速度约为35 KB/s1024B / 28500cycles * 168MHz生成速度约42 KB/s所有操作耗时与输入长度呈严格线性关系无平方级算法在 128KB Flash 的 STM32G0B1RE 上json_lite占用 Flash 比例仅2.2%为 OTA、远程诊断等关键功能留出充足空间。7. 与其他嵌入式 JSON 库对比特性json_litecJSONjsmnArduinoJsonFlash 占用2.8–4.2 KB12–18 KB3.5 KB8–15 KBRAM运行时12 B固定1–4 KB动态16 B固定1–8 KB动态动态内存分配❌ 零 malloc✅ 必需❌ 零 malloc✅ 可选但默认启用SAX 模式✅ 原生支持❌ 无✅ 原生支持❌ 无生成器✅ 内置✅ 内置❌ 无✅ 内置浮点支持✅可裁剪✅❌ 仅整数✅Unicode 支持❌ASCII only✅❌ASCII only✅许可证MITMITMITMIT适用场景资源极度受限、安全关键、实时系统通用嵌入式、Linux 应用超低资源、仅解析Arduino 生态、快速原型选择json_lite的决策点非常明确当项目对确定性、内存确定性、代码尺寸的要求高于对Unicode 支持、DOM 树遍历便利性的需求时它是经过严苛工程验证的最优解。在某工业 PLC 的 Modbus TCP 网关固件中替换cJSON为json_lite后堆内存峰值从 3.2KB 降至 0JSON 解析任务 worst-case 执行时间从 8.7ms 稳定至 1.2ms成功通过第三方功能安全认证。