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.ccJSON.h
  • 集成到项目
    • cJSON.ccJSON.h 加入嵌入式项目。
    • 在编译器中确保包含标准 C 库(如 string.hstdlib.h)。
    • 对于资源极度受限的系统,可通过修改 cJSON.h 中的宏(如禁用某些功能)优化内存占用。
  • 编译:将 cJSON 源码编译到你的嵌入式项目中,通常只需添加 cJSON.c 到项目文件列表。

2. 使用步骤

  1. 解析 JSON 字符串
    • 使用 cJSON_Parse 解析 JSON 字符串。
    • 检查解析结果是否为空(防止无效 JSON)。
    • 使用 cJSON_GetObjectItemcJSON_GetArrayItem 提取数据。
  2. 生成 JSON 数据
    • 使用 cJSON_Create* 函数创建 JSON 节点。
    • 使用 cJSON_AddItemTo* 函数构建对象或数组。
    • 使用 cJSON_Print 转换为 JSON 字符串。
  3. 释放内存
    • 使用 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 提取 temperaturehumiditystatus 的值。
  • 检查每个字段是否存在,防止访问空指针。
  • 最后用 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_CreateObjectcJSON_CreateArray 创建 JSON 结构。
  • 通过 cJSON_Add* 函数添加键值对和数组元素。
  • cJSON_Print 生成 JSON 字符串,供设备通过网络发送。
  • 释放 rootjson_str 的内存,防止泄漏。
  • 适用场景:嵌入式设备(如 ESP32)生成状态数据,通过 MQTT 或 HTTP 发送到云端。

输出

{
	"device_id":	"DEV001",
	"battery":	85,
	"sensors":	[{
			"type":	"temp",
			"value":	26.2
		}, {
			"type":	"hum",
			"value":	58
		}]
}

五、在嵌入式系统中的使用注意事项

  1. 内存管理

    • 嵌入式系统内存有限,始终使用 cJSON_Delete 释放 cJSON 结构。
    • 使用 cJSON_PrintUnformatted 生成无格式 JSON 字符串,节省空间。
    • 对于极低内存设备,可通过 #define cJSON_Malloc 自定义内存分配函数,优化内存使用。
  2. 错误处理

    • 检查 cJSON_Parse 返回值是否为 NULL。
    • 使用 cJSON_Is* 函数(如 cJSON_IsNumbercJSON_IsString)验证字段类型。
  3. 性能优化

    • 避免频繁解析和生成大型 JSON 数据。
    • 对于固定格式的 JSON,可使用静态结构减少动态分配。
  4. 移植性

    • cJSON 默认使用标准 C 库的 mallocfree,确保嵌入式平台的 C 库支持这些函数。
    • 若平台不支持浮点数运算,可禁用 cJSON_Number 的浮点支持(修改 cJSON.h)。

六、适用场景和经典案例

  1. 物联网设备与云端通信

    • 场景:ESP32 采集温湿度数据,通过 MQTT 协议以 JSON 格式发送到 AWS IoT。
    • 实现:使用示例 2 生成 JSON 数据,调用 MQTT 库发送。
    • 优点:JSON 格式通用,易于云端解析;cJSON 轻量,适合 ESP32 的 520KB SRAM。
  2. 设备配置管理

    • 场景:STM32 设备从 Flash 读取 JSON 格式的配置文件,调整运行参数。
    • 实现:使用示例 1 解析 JSON,提取参数值。
    • 优点:JSON 格式直观,易于手动编辑配置文件。
  3. 调试日志输出

    • 场景:设备通过串口输出 JSON 格式的运行状态,便于调试工具解析。
    • 实现:类似示例 2,生成 JSON 后通过 UART 输出。
    • 优点:结构化日志便于自动化分析。

七、总结

cJSON 是一个轻量、简单、高效的 JSON 解析和生成库,非常适合嵌入式系统。其核心优势在于无依赖、易移植和低资源占用。通过解析和生成 JSON,cJSON 能满足物联网设备通信、配置管理等需求。使用时需注意内存管理和错误处理,尤其在资源受限的嵌入式环境中。

posted @ 2025-07-09 16:45  WJBDAN  阅读(226)  评论(0)    收藏  举报