cjson的介绍以及使用方式?
在嵌入式系统中,JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,常用于设备与服务器之间的数据通信。cJSON 是一个用 C 语言实现的轻量级 JSON 解析和生成库,因其简单高效、资源占用低,非常适合嵌入式系统。下面我将详细介绍 cJSON 的功能、使用方法、适用场景,并通过经典示例帮助你理解其应用。
一、cJSON 是什么?为什么用它?
1. cJSON 简介
cJSON 是一个开源的 C 语言库,专门用于解析和生成 JSON 数据。它由 Dave Gamble 开发,代码简洁,易于移植,无需外部依赖,特别适合资源受限的嵌入式系统。cJSON 的主要功能包括:
- 解析 JSON:将 JSON 字符串解析为 C 语言中的数据结构。
- 生成 JSON:将 C 语言中的数据结构转换为 JSON 字符串。
- 操作 JSON:支持增删改查 JSON 对象、数组等。
2. 为什么在嵌入式系统中使用 cJSON?
- 轻量级:cJSON 代码量小,编译后占用空间少,适合内存受限的嵌入式设备(如 MCU)。
- 无依赖:纯 C 实现,易于移植到各种嵌入式平台(如 STM32、ESP32 等)。
- 高效:解析和生成 JSON 的性能较高,满足实时性要求。
- 简单易用:API 设计直观,开发者可以快速上手。
- 开源免费:MIT 许可证,适合商业和非商业项目。
3. 适用场景
cJSON 常用于以下嵌入式场景:
- 物联网(IoT)设备:设备与云端通过 JSON 格式交换数据,如传感器数据上传、远程配置下发。
- 通信协议:在 HTTP、MQTT、WebSocket 等协议中,JSON 作为数据载体,cJSON 用于处理这些数据。
- 配置文件解析:嵌入式设备使用 JSON 格式存储配置参数,cJSON 用于读取和修改。
- 调试和日志:将设备状态以 JSON 格式输出,便于调试和分析。
二、cJSON 的核心功能和数据结构
1. cJSON 的数据结构
cJSON 使用一个核心结构体 cJSON
来表示 JSON 数据,支持以下类型:
- 基本类型:
cJSON_Number
(数字)、cJSON_String
(字符串)、cJSON_True
/cJSON_False
(布尔值)、cJSON_Null
(空值)。 - 复合类型:
cJSON_Object
(对象)、cJSON_Array
(数组)。
cJSON
结构体的定义(简化版)如下:
typedef struct cJSON {
struct cJSON *next, *prev; // 用于链表,数组或对象中的元素链接
struct cJSON *child; // 子节点(对象或数组的子元素)
int type; // 数据类型(如 cJSON_Number、cJSON_String 等)
char *valuestring; // 字符串值
int valueint; // 整数值
double valuedouble; // 浮点数值
char *string; // 键名(仅对象中使用)
} cJSON;
2. 核心 API
以下是 cJSON 常用的 API 函数,分为解析、生成和操作三大类:
- 解析 JSON:
cJSON_Parse(const char *value)
:将 JSON 字符串解析为 cJSON 结构。cJSON_GetObjectItem(cJSON *object, const char *string)
:获取对象中指定键的值。cJSON_GetArrayItem(cJSON *array, int index)
:获取数组中指定索引的元素。
- 生成 JSON:
cJSON_CreateObject()
:创建空 JSON 对象。cJSON_CreateArray()
:创建空 JSON 数组。cJSON_CreateString(const char *string)
:创建字符串节点。cJSON_CreateNumber(double num)
:创建数字节点。cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
:向对象添加键值对。cJSON_Print(cJSON *item)
:将 cJSON 结构转换为 JSON 字符串。
- 操作 JSON:
cJSON_Delete(cJSON *item)
:释放 cJSON 结构内存。cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
:替换对象中的键值。cJSON_DetachItemFromArray(cJSON *array, int which)
:从数组中移除元素。
三、如何使用 cJSON?
1. 环境准备
- 获取 cJSON:从 GitHub(
https://github.com/DaveGamble/cJSON
)下载cJSON.c
和cJSON.h
。 - 集成到项目:
- 将
cJSON.c
和cJSON.h
加入嵌入式项目。 - 在编译器中确保包含标准 C 库(如
string.h
、stdlib.h
)。 - 对于资源极度受限的系统,可通过修改
cJSON.h
中的宏(如禁用某些功能)优化内存占用。
- 将
- 编译:将 cJSON 源码编译到你的嵌入式项目中,通常只需添加
cJSON.c
到项目文件列表。
2. 使用步骤
- 解析 JSON 字符串:
- 使用
cJSON_Parse
解析 JSON 字符串。 - 检查解析结果是否为空(防止无效 JSON)。
- 使用
cJSON_GetObjectItem
或cJSON_GetArrayItem
提取数据。
- 使用
- 生成 JSON 数据:
- 使用
cJSON_Create*
函数创建 JSON 节点。 - 使用
cJSON_AddItemTo*
函数构建对象或数组。 - 使用
cJSON_Print
转换为 JSON 字符串。
- 使用
- 释放内存:
- 使用
cJSON_Delete
释放 cJSON 结构,避免内存泄漏。
- 使用
四、经典示例
以下通过两个经典嵌入式场景示例,展示 cJSON 的使用方法。
示例 1:解析传感器数据的 JSON 字符串
假设设备接收到以下 JSON 数据,表示传感器状态:
{
"temperature": 25.5,
"humidity": 60,
"status": "OK"
}
代码实现:
#include <stdio.h>
#include <string.h>
#include "cJSON.h"
void parse_sensor_data(const char *json_str) {
// 解析 JSON 字符串
cJSON *root = cJSON_Parse(json_str goroutine
if (root == NULL) {
printf("Failed to parse JSON\n");
return;
}
// 提取数据
cJSON *temp = cJSON_GetObjectItem(root, "temperature");
cJSON *hum = cJSON_GetObjectItem(root, "humidity");
cJSON *status = cJSON_GetObjectItem(root, "status");
if (temp && hum && status) {
printf("Temperature: %.1f\n", temp->valuedouble);
printf("Humidity: %d\n", hum->valueint);
printf("Status: %s\n", status->valuestring);
} else {
printf("Missing fields in JSON\n");
}
// 释放内存
cJSON_Delete(root);
}
int main() {
const char *json_str = "{\"temperature\": 25.5, \"humidity\": 60, \"status\": \"OK\"}";
parse_sensor_data(json_str);
return 0;
}
解释:
cJSON_Parse
将 JSON 字符串解析为 cJSON 结构。- 使用
cJSON_GetObjectItem
提取temperature
、humidity
和status
的值。 - 检查每个字段是否存在,防止访问空指针。
- 最后用
cJSON_Delete
释放内存。 - 适用场景:嵌入式设备从云端接收 JSON 格式的传感器数据,解析后用于控制逻辑(如判断温度是否超标)。
输出:
Temperature: 25.5
Humidity: 60
Status: OK
示例 2:生成设备状态的 JSON 字符串
假设设备需要生成以下 JSON 数据发送到服务器:
{
"device_id": "DEV001",
"battery": 85,
"sensors": [
{"type": "temp", "value": 26.2},
{"type": "hum", "value": 58}
]
}
代码实现:
#include <stdio.h>
#include <string.h>
#include "cJSON.h"
void create_device_status() {
// 创建 JSON 对象
cJSON *root = cJSON_CreateObject();
// 添加基本字段
cJSON_AddStringToObject(root, "device_id", "DEV001");
cJSON_AddNumberToObject(root, "battery", 85);
// 创建传感器数组
cJSON *sensors = cJSON_CreateArray();
cJSON_AddItemToObject(root, "sensors", sensors);
// 添加第一个传感器数据
cJSON *sensor1 = cJSON_CreateObject();
cJSON_AddStringToObject(sensor1, "type", "temp");
cJSON_AddNumberToObject(sensor1, "value", 26.2);
cJSON_AddItemToArray(sensors, sensor1);
// 添加第二个传感器数据
cJSON *sensor2 = cJSON_CreateObject();
cJSON_AddStringToObject(sensor2, "type", "hum");
cJSON_AddNumberToObject(sensor2, "value", 58);
cJSON_AddItemToArray(sensors, sensor2);
// 转换为 JSON 字符串
char *json_str = cJSON_Print(root);
printf("%s\n", json_str);
// 释放内存
cJSON_Delete(root);
free(json_str);
}
int main() {
create_device_status();
return 0;
}
解释:
- 使用
cJSON_CreateObject
和cJSON_CreateArray
创建 JSON 结构。 - 通过
cJSON_Add*
函数添加键值对和数组元素。 cJSON_Print
生成 JSON 字符串,供设备通过网络发送。- 释放
root
和json_str
的内存,防止泄漏。 - 适用场景:嵌入式设备(如 ESP32)生成状态数据,通过 MQTT 或 HTTP 发送到云端。
输出:
{
"device_id": "DEV001",
"battery": 85,
"sensors": [{
"type": "temp",
"value": 26.2
}, {
"type": "hum",
"value": 58
}]
}
五、在嵌入式系统中的使用注意事项
-
内存管理:
- 嵌入式系统内存有限,始终使用
cJSON_Delete
释放 cJSON 结构。 - 使用
cJSON_PrintUnformatted
生成无格式 JSON 字符串,节省空间。 - 对于极低内存设备,可通过
#define cJSON_Malloc
自定义内存分配函数,优化内存使用。
- 嵌入式系统内存有限,始终使用
-
错误处理:
- 检查
cJSON_Parse
返回值是否为 NULL。 - 使用
cJSON_Is*
函数(如cJSON_IsNumber
、cJSON_IsString
)验证字段类型。
- 检查
-
性能优化:
- 避免频繁解析和生成大型 JSON 数据。
- 对于固定格式的 JSON,可使用静态结构减少动态分配。
-
移植性:
- cJSON 默认使用标准 C 库的
malloc
和free
,确保嵌入式平台的 C 库支持这些函数。 - 若平台不支持浮点数运算,可禁用
cJSON_Number
的浮点支持(修改cJSON.h
)。
- cJSON 默认使用标准 C 库的
六、适用场景和经典案例
-
物联网设备与云端通信:
- 场景:ESP32 采集温湿度数据,通过 MQTT 协议以 JSON 格式发送到 AWS IoT。
- 实现:使用示例 2 生成 JSON 数据,调用 MQTT 库发送。
- 优点:JSON 格式通用,易于云端解析;cJSON 轻量,适合 ESP32 的 520KB SRAM。
-
设备配置管理:
- 场景:STM32 设备从 Flash 读取 JSON 格式的配置文件,调整运行参数。
- 实现:使用示例 1 解析 JSON,提取参数值。
- 优点:JSON 格式直观,易于手动编辑配置文件。
-
调试日志输出:
- 场景:设备通过串口输出 JSON 格式的运行状态,便于调试工具解析。
- 实现:类似示例 2,生成 JSON 后通过 UART 输出。
- 优点:结构化日志便于自动化分析。
七、总结
cJSON 是一个轻量、简单、高效的 JSON 解析和生成库,非常适合嵌入式系统。其核心优势在于无依赖、易移植和低资源占用。通过解析和生成 JSON,cJSON 能满足物联网设备通信、配置管理等需求。使用时需注意内存管理和错误处理,尤其在资源受限的嵌入式环境中。