cJSON 解析
cJSON v1.7.15
学习目标
- 理解 cJSON 的核心用途——解析云端下发的 JSON 配置、构造传感器数据上报的 JSON 报文
- 掌握 JSON 解析:
cJSON_Parse()→cJSON_GetObjectItem()→ 提取值 →cJSON_Delete() - 掌握 JSON 构造:
cJSON_CreateObject()→cJSON_AddXxxToObject()→cJSON_Print()→cJSON_Delete() - 理解 cJSON 内存管理——每次 Parse/Create 必须对应 Delete/Free
基本概念
cJSON 的设计理念
轻量级单文件 JSON 解析器——树状结构,cJSON_Parse() 将 JSON 字符串解析为 cJSON 节点树,通过 key 查找子节点。
flowchart TD
JSON["{temp:25.5, humidity:60}"] --> PARSE[cJSON_Parse]
PARSE --> ROOT[cJSON 根节点]
ROOT --> TEMP["cJSON_GetObjectItem(root, temp)"]
ROOT --> HUM["cJSON_GetObjectItem(root, humidity)"]
TEMP --> V1["item->valueint = 25"]
HUM --> V2["item->valuedouble = 60.0"]
V1 --> DEL[cJSON_Delete(root)]
V2 --> DEL
典型使用场景
| 场景 | 说明 |
|---|---|
| 云端下发配置 | MQTT 收到 {"interval":5000} → 解析 interval |
| 传感器上报 | 构造 {"temp":25.5,"humidity":60} → MQTT publish |
| OTA 元数据 | 解析 {"version":"2.0.1","size":524288} |
解析 vs 构造
解析:JSON 字符串 → cJSON_Parse() → cJSON 树 → cJSON_GetObjectItem() 提取值
构造:cJSON_CreateObject() → cJSON_AddXxxToObject() → cJSON_Print() → JSON 字符串
内存管理陷阱
每个 cJSON_Parse() 返回的根节点必须用 cJSON_Delete(root) 释放。cJSON_Print() 返回的字符串必须用 cJSON_free(str) 释放。忘记释放是内存泄漏最常见的原因。
涉及 API
| API | 用途 | 内存 |
|---|---|---|
cJSON_Parse(str) |
将 JSON 字符串解析为节点树 | 需 Delete |
cJSON_GetObjectItem(obj, key) |
按 key 查找子节点 | — |
cJSON_GetArraySize(arr) |
获取数组长度 | — |
cJSON_GetArrayItem(arr, index) |
按索引取数组元素 | — |
cJSON_GetStringValue(item) |
获取 String 值 | — |
cJSON_IsNumber(item) / item->valueint / item->valuedouble |
获取 Number 值 | — |
cJSON_CreateObject() |
创建 {} |
需 Delete |
cJSON_CreateArray() |
创建 [] |
需 Delete |
cJSON_AddStringToObject(obj, key, val) |
添加 String 字段 | — |
cJSON_AddNumberToObject(obj, key, num) |
添加 Number 字段 | — |
cJSON_AddItemToArray(arr, item) |
数组添加元素 | — |
cJSON_Print(item) |
序列化为 JSON 字符串 | 需 free |
cJSON_PrintUnformatted(item) |
序列化(无缩进) | 需 free |
cJSON_Delete(item) |
释放节点树 | — |
cJSON_free(ptr) |
释放 Print 返回的字符串 | — |
案例说明
做什么
子场景 1 — 解析云端配置:串口输入 {"interval": 5000, "threshold": 80} → 解析 interval 和 threshold → 应用到设备参数。
子场景 2 — 构造上报数据:读取传感器 → 构造 {"temp": 25.5, "humidity": 60} → 串口打印 JSON。
案例操作指导
集成 cJSON
添加 cJSON.c / cJSON.h 到工程,或在 Kconfig 中启用 cJSON 组件。
编译
验证
串口输入 {"threshold":80} → 解析输出 threshold = 80。构造的温度 JSON 正确输出。
关键配置
| 参数 | 值 | 说明 |
|---|---|---|
| 嵌套深度限制 | 256 | CJSON_NESTING_LIMIT,超出则解析失败 |
| 数字精度 | double |
item->valuedouble;整数用 item->valueint |
| 单节点内存 | ~64B | 100 个节点 ≈ 6.4KB |
代码详解
JSON 解析标准流程
#include "cJSON.h"
void parse_config(const char *json_str) {
/* 1. 解析 */
cJSON *root = cJSON_Parse(json_str);
if (root == NULL) {
const char *err = cJSON_GetErrorPtr();
printf("parse error at: %s\n", err ? err : "unknown");
return;
}
/* 2. 提取字段——检查类型后取值 */
cJSON *item = cJSON_GetObjectItem(root, "interval");
if (item != NULL && cJSON_IsNumber(item)) {
int interval = item->valueint;
printf("interval = %d\n", interval);
}
item = cJSON_GetObjectItem(root, "threshold");
if (item != NULL && cJSON_IsNumber(item)) {
double threshold = item->valuedouble;
printf("threshold = %.1f\n", threshold);
}
/* 3. 释放 */
cJSON_Delete(root); // 别忘了!
}
JSON 构造标准流程
void build_report(double temp, double humidity) {
/* 1. 创建根对象 */
cJSON *root = cJSON_CreateObject();
/* 2. 添加字段 */
cJSON_AddNumberToObject(root, "temp", temp);
cJSON_AddNumberToObject(root, "humidity", humidity);
/* 3. 序列化 */
char *str = cJSON_PrintUnformatted(root);
printf("report: %s\n", str);
/* 4. 释放——顺序:先 free 字符串,再 Delete 树 */
cJSON_free(str);
cJSON_Delete(root);
}
嵌套 JSON 解析
cJSON *sensor = cJSON_GetObjectItem(root, "sensor");
if (sensor != NULL) {
cJSON *temp = cJSON_GetObjectItem(sensor, "temp");
printf("sensor.temp = %d\n", temp->valueint);
}
数组遍历
cJSON *arr = cJSON_GetObjectItem(root, "devices");
if (arr != NULL && cJSON_IsArray(arr)) {
int count = cJSON_GetArraySize(arr);
for (int i = 0; i < count; i++) {
cJSON *item = cJSON_GetArrayItem(arr, i);
cJSON *name = cJSON_GetObjectItem(item, "name");
printf("device[%d]: %s\n", i, cJSON_GetStringValue(name));
}
}
内存泄漏检测
/* 每个 Parse / Create 必须匹配 Delete */
cJSON *root1 = cJSON_Parse(str1); // alloc
// ... 使用 ...
cJSON_Delete(root1); // free —— 不能少!
cJSON *root2 = cJSON_CreateObject(); // alloc
char *s = cJSON_Print(root2); // alloc
// ... 发送 s ...
cJSON_free(s); // free —— 不能少!
cJSON_Delete(root2); // free —— 不能少!