JSON处理C函数库-cJSON
libcjson是一个用于 JSON 数据编解码的轻量级C函数库,文件只有 500 多行,速度理想。其代码维护良好,结构简单易懂,对于想要学习 C 语言项目或者处理简单 JSON 数据的开发者来说是一个很好的选择。虽然功能不是极其强大,但在很多对性能要求较高且 JSON 操作不特别复杂的场景下非常适用。采用MIT协议授权。
libcjson is an ultralightweight JSON parser in ANSI C. cJSON is written in ANSI C (or C89, C90). If your compiler or C library doesn't follow this standard, correct behavior is not guaranteed.
适用于小型项目或者对资源占用要求严格的系统中,用于 JSON 数据的生成和解析,比如在嵌入式系统中与其他设备进行简单的 JSON 格式数据通信,或者在一些轻量级的应用程序中处理配置文件等。使用uJson库有两种方式:使用编译好的动态库或者静态库;将源码导入到自己工程中一起参与编译,cJson.c和cJson.h。
核心功能:
基本类型
null is created with cJSON_CreateNull,
booleans are created with cJSON_CreateTrue, cJSON_CreateFalse or cJSON_CreateBool,
numbers are created with cJSON_CreateNumber. This will set both valuedouble and valueint. If the number is outside of the range of an integer, INT_MAX or INT_MIN are used for valueint,
strings are created with cJSON_CreateString (copies the string) or with cJSON_CreateStringReference (directly points to the string. This means that valuestring won't be deleted by cJSON_Delete and you are responsible for its lifetime, useful for constants).
另外有Arrays、Objects类型。
由于C语言是强类型语言,任何json中的独立属性对于C实现来说都视为一个独立的对象,然后用指针将这些对象联系起来;具体的话,可以直接阅读cJson.h的函数,都很直观。使用时一定要注意动态内存的释放!尽管json更现代,但是在实际嵌入式平台,如果没有系统内存管理支撑,不建议用c去处理json数据,动态内存分配着实有点令人担心。
克隆代码仓库后在项目文件夹下新建build文件夹,注意这里的编译器要与最终你项目的编译器统一;在build文件夹下执行Cmake生成makefile文件。
cJSON\build>cmake .. -G "Unix Makefiles" -DENABLE_CJSON_TEST=off
也可执行make编译,生成cJson动态库。在build文件夹下生成dll动态库,动态库的特点就是使用的时候不链接的,运行的程序的时候再调用。所以这个dll文件需要放到跟可执行程序相同目录下。其实这个库只有一个cJson.c文件,可以在自己项目中一起编译。
集成步骤
第1步:从 GitHub 下载源码
第2步:把 cJSON.h 和 cJSON.c 扔进你的项目目录
第3步:在代码里加一行
#include "cJSON.h"
如果把文件放在其它目录里,比如 /freeoa/libs/cjson/,那就改成:
#include "/freeoa/libs/cjson/cJSON.h"
在 cJSON 的世界里,不管是对象、数组,还是一个简单的数字,都用同一个结构体 cJSON 来表示。C 语言处理JSON,最怕的就是内存泄漏。
常用的类型与函数对应
假设要生成这样一段 JSON:
{
"name": "Awesome Scren",
"resolutions": [
{"width": 1280, "height": 720},
{"width": 1920, "height": 1080},
{"width": 3840, "height": 2160}
]
}
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
char* create_monitor_json(void) {
// 1. 创建根对象
cJSON *monitor = cJSON_CreateObject();
// 2. 添加名称字段
cJSON_AddStringToObject(monitor, "name", "Awesome Scren");
// 3. 创建分辨率数组
cJSON *resolutions = cJSON_AddArrayToObject(monitor, "resolutions");
// 4. 往数组里塞数据
int res_data[3][2] = {{1280, 720}, {1920, 1080}, {3840, 2160}};
for (int i = 0; i < 3; i++) {
cJSON *res = cJSON_CreateObject();
cJSON_AddNumberToObject(res, "width", res_data[i][0]);
cJSON_AddNumberToObject(res, "height", res_data[i][1]);
cJSON_AddItemToArray(resolutions, res);
}
// 5. 转成字符串
char *json_str = cJSON_Print(monitor);
// 6. 释放 cJSON 对象
cJSON_Delete(monitor);
return json_str; // 调用者需要 free 这个字符串
}
int main(void) {
char *result = create_monitor_json();
printf("%s\n", result);
free(result); // 别忘了释放!
return 0;
}
构建流程图解

打印函数
• cJSON_Print():生成带缩进的格式化字符串,适合调试和阅读;
• cJSON_PrintUnformatted():生成紧凑字符串,适合网络传输。
注意:cJSON_Print() 返回的字符串是在堆上分配的,用完必须 free() 进行释放,否则内存泄漏。
解析外部的 JSON 字符串
上面演示创建 JSON,那收到一串 JSON 字符串怎么读取呢?
核心函数:cJSON_Parse
cJSON *root = cJSON_Parse(json_string);
这个函数会把字符串解析成一棵 cJSON 对象树。
但有一个关键点:一定要检查返回值是不是 NULL(这也是C中常见的基本操作了)。
cJSON *root = cJSON_Parse(json_string);
if (root == NULL) {
// 解析失败,可能是 JSON 格式不对
const char *error = cJSON_GetErrorPtr();
printf("解析错误:%s\n", error);
return;
}
不检查直接用,程序会直接崩溃。完整的示例参考下文的示例3。
提取数据
1:安全访问对象成员
cJSON *name = cJSON_GetObjectItemCaseSensitive(root, "name");
这个函数有个很棒的特性:自带空指针检查。即使 root 是 NULL,它也不会崩溃,而是返回 NULL。
这意味着你可以安全地链式调用:
// 即使中间某一层是 NULL 也不会崩
cJSON *city = cJSON_GetObjectItemCaseSensitive(
cJSON_GetObjectItemCaseSensitive(root, "address"),
"city"
);
2:类型检查
拿到数据后,别急着用,先问一句"是什么类型":
if (cJSON_IsString(name) && name->valuestring != NULL) {
printf("名称:%s\n", name->valuestring);
}
if (cJSON_IsNumber(price)) {
printf("价格:%.2f\n", price->valuedouble);
}
这是防御性编程的好习惯。万一服务器返回的数据结构和你预期的不一样,至少程序不会直接挂掉。
3:高效遍历数组
cJSON *item = NULL;
cJSON_ArrayForEach(item, resolutions) {
cJSON *width = cJSON_GetObjectItemCaseSensitive(item, "width");
cJSON *height = cJSON_GetObjectItemCaseSensitive(item, "height");
// 处理每个分辨率...
}
cJSON_ArrayForEach 是一个宏,用起来就像 for 循环一样自然。但有两条原则必须要遵守(也是C编程的基本操作):
原则1:谁创建,谁释放
不管是用 cJSON_Parse() 解析出来的,还是用 cJSON_CreateObject() 创建的,只要是顶层对象,用完必须调用 cJSON_Delete()。
// 解析场景
cJSON *root = cJSON_Parse(json_str);
// ... 使用 root ...
cJSON_Delete(root); // 必须释放
// 创建场景
cJSON *obj = cJSON_CreateObject();
// ... 使用 obj ...
cJSON_Delete(obj); // 必须释放
cJSON_Delete() 会递归释放整棵树,包括所有子节点。
原则2:所有权转移
当把一个节点用 cJSON_AddItemToObject() 或 cJSON_AddItemToArray() 添加到另一个对象里时,它的"主动权"就是别人的了。
正确做法:
cJSON *root = cJSON_CreateObject();
cJSON *child = cJSON_CreateString("hello");
cJSON_AddItemToObject(root, "greeting", child);
// child 的所有权已经转移给 root 了
// ... 使用 root ...
cJSON_Delete(root); // 只需要释放 root,child 会被自动释放
错误做法(程序会崩!):
cJSON *root = cJSON_CreateObject();
cJSON *child = cJSON_CreateString("hello");
cJSON_AddItemToObject(root, "greeting", child);
//错误!child 已经属于 root 了
cJSON_Delete(child); // 这里释放了一次
//错误!root 删除时会再释放 child
cJSON_Delete(root); // 重复释放,程序崩溃!
这叫重复释放(double free),是 C 语言内存管理中最经典的坑之一。
也即:
正确操作
1.Parse/Create的顶层对象>必须Delete;
2.子节点 Add 到父节点后>只 Delete 父节点;
3.CJSON Print的返回值→必须 free。
错误操作
1.Add 之后又 Delete 子节点 → 重复释放,崩溃;
2.忘记 Delete 顶层对象 → 内存泄漏;
3.忘记 free 打印的字符串 → 内存泄漏。
核心 API 速查
编码示例1
#include <stdio.h>
#include "cJSON.h"
int main() {
// 创建一个JSON对象
cJSON *root = cJSON_CreateObject();
if (root == NULL) {
return 1;
}
// 在JSON对象中添加一个字符串类型的键值对
cJSON_AddItemToObject(root, "name", cJSON_CreateString("John"));
// 将JSON对象转换为字符串
char *json_string = cJSON_Print(root);
if (json_string == NULL) {
cJSON_Delete(root);
return 1;
}
// 打印JSON字符串
printf("%s\n", json_string);
// 释放内存
cJSON_Delete(root);
free(json_string);
return 0;
}
解码示例
#include <stdio.h>
#include "cJSON.h"
int main() {
const char *json_string = "{\"name\":\"John\"}";
// 解析JSON字符串
cJSON *root = cJSON_Parse(json_string);
if (root == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr!= NULL) {
printf("Error before: %s\n", error_ptr);
}
return 1;
}
// 获取"name"键对应的值
cJSON *name_json = cJSON_GetObjectItem(root, "name");
if (cJSON_IsString(name_json)) {
printf("Name: %s\n", name_json->valuestring);
}
// 释放内存
cJSON_Delete(root);
return 0;
}
编码示例2
封装Json结构并打印我们假定一个json数据结构体:{
"APP_IS_VALID": true,
"REQUEST_UPDATE": false,
"APP_INFO": {
"VERSION": "0.00.001",
"RELEASE_DATE": "2025-05-16",
"SH256": "0123456789abcdef"
},
"CONFIGURATION_TYPE_LIST": ["High","Normal","Low"]
}
build中存放编译相关文件,记得拷贝cJson.dll到build文件夹,在main.c中实现测试。
#include <stdio.h>
#include <stddef.h>
#include "cJson/cJson.h"
#include <stdlib.h>
void demo1(void) {
cJSON *APP_INFO = cJSON_CreateObject();
cJSON_AddItemToObject(APP_INFO, "VERSION", cJSON_CreateString("0.00.001"));
cJSON_AddItemToObject(APP_INFO, "RELEASE_DATE", cJSON_CreateString("2025-05-16"));
cJSON_AddItemToObject(APP_INFO, "SH256", cJSON_CreateString("0123456789abcdef"));
cJSON* CONFIGURATION_TYPE_LIST = cJSON_CreateArray();
cJSON_AddItemToArray(CONFIGURATION_TYPE_LIST, cJSON_CreateString("High"));
cJSON_AddItemToArray(CONFIGURATION_TYPE_LIST, cJSON_CreateString("Normal"));
cJSON_AddItemToArray(CONFIGURATION_TYPE_LIST, cJSON_CreateString("Low"));
cJSON* GLOBAL_CFG = cJSON_CreateObject();
cJSON_AddItemToObject(GLOBAL_CFG, "APP_IS_VALID", cJSON_CreateBool(0));
cJSON_AddItemToObject(GLOBAL_CFG, "REQUEST_UPDATE", cJSON_CreateBool(0));
cJSON_AddItemToObject(GLOBAL_CFG, "APP_INFO", APP_INFO);
cJSON_AddItemToObject(GLOBAL_CFG, "CONFIGURATION_TYPE_LIST", CONFIGURATION_TYPE_LIST);
char *cPrint = cJSON_Print(GLOBAL_CFG);
char *cPrintUnformatted = cJSON_PrintUnformatted(GLOBAL_CFG);
printf("cJSON_Print:\n%s\n", cPrint);
printf("cJSON_PrintUnformatted:\n%s\n", cPrintUnformatted);
// 记得使用cJSON_Print 和 cJSON_PrintUnformatted返回来的字符指针需要free掉内存!
free(cPrint);
free(cPrintUnformatted);
}
int main(void) {
demo1();
}
编译通过后直接执行可以看到结果。
解析Json文件并获取属性
解析时从严谨和安全角度,需要使用结构体中的type类型进行判断,再取数据或者对象;可以使用cJSON_Print函数去获取字符串,也可以使用结构体中的valuestring取值,但要注意的是,使用cJSON_Print函数去获取字符串之后需要free掉获取到的指针,否则会造成内存泄漏。
#include <string.h>
#include <sys/stat.h>
#include <stdbool.h>
void demo2() {
/* open json file*/
FILE *file = NULL;
file = fopen("../cfg.json", "r");
if (file == NULL) {
printf("Open file failed!\n");
return;
}
/* fetch file's size */
struct stat statBuffer;
stat("../cfg.json", &statBuffer);
int fileSize = statBuffer.st_size;
/* malloc memory*/
char *jsonStr = (char *)malloc(sizeof(char) * fileSize + 1);
memset(jsonStr, 0, fileSize + 1);
/* read string */
int size = fread(jsonStr, sizeof(char), fileSize, file);
if (size == 0) {
printf("Read file Failed!\n");
fclose(file);
return;
}
printf("%s\n", jsonStr);
fclose(file);
/* parser string to jsonString*/
cJSON *root = cJSON_Parse(jsonStr);
if (!root) {
printf("Error before: [%s]\n", cJSON_GetErrorPtr());
free(jsonStr);
return;
}
free(jsonStr);
cJSON *item = NULL;
char *v_str = NULL;
double v_double = 0.0;
int v_int = 0;
bool v_bool = false;
/* 获取APP_INFO对象 */
item = cJSON_GetObjectItem(root, "APP_INFO");
if (item != NULL) {
/* 判断是否是新的json对象 */
if (item->type == cJSON_Object) {
cJSON* version = NULL;
/* 获取VERSION对象 */
version = cJSON_GetObjectItem(item, "VERSION");
if (version->type == cJSON_String) {
v_str = version->valuestring;
printf("VERSION = %s\n", v_str);
}
}
}
}
int main(void) {
demo2();
}
在上面的示例中,先从cfg.json文件中一次性读取所有的字符信息,然后将字符一次性转换成jsonString然后解析。这里以获取APP的版本号为例。
编码示例3(检查显示器是否支持 1080P)
#include <stdio.h>
#include "cJSON.h"
// 返回 1 表示支持,0 表示不支持
int supports_full_hd(const char *json_str) {
cJSON *root = cJSON_Parse(json_str);
// 解析失败检查
if (root == NULL) {
printf("JSON 解析失败\n");
return 0;
}
// 获取显示器名称(可选)
cJSON *name = cJSON_GetObjectItemCaseSensitive(root, "name");
if (cJSON_IsString(name)) {
printf("正在检查显示器:%s\n", name->valuestring);
}
// 遍历分辨率数组
cJSON *resolutions = cJSON_GetObjectItemCaseSensitive(root, "resolutions");
cJSON *res = NULL;
cJSON_ArrayForEach(res, resolutions) {
cJSON *w = cJSON_GetObjectItemCaseSensitive(res, "width");
cJSON *h = cJSON_GetObjectItemCaseSensitive(res, "height");
if (cJSON_IsNumber(w) && cJSON_IsNumber(h)) {
if (w->valuedouble == 1920 && h->valuedouble == 1080) {
cJSON_Delete(root); // 别忘了释放
return 1; // 找到了!
}
}
}
cJSON_Delete(root); // 别忘了释放
return 0;
}
最新版本:1.7
v1.7.8于2024年5月发布。
项目主页:https://github.com/DaveGamble/cJSON
libcjson is an ultralightweight JSON parser in ANSI C. cJSON is written in ANSI C (or C89, C90). If your compiler or C library doesn't follow this standard, correct behavior is not guaranteed.
适用于小型项目或者对资源占用要求严格的系统中,用于 JSON 数据的生成和解析,比如在嵌入式系统中与其他设备进行简单的 JSON 格式数据通信,或者在一些轻量级的应用程序中处理配置文件等。使用uJson库有两种方式:使用编译好的动态库或者静态库;将源码导入到自己工程中一起参与编译,cJson.c和cJson.h。
核心功能:
| 特性 | 说明 |
| 超轻量 | 整个库就一个 cJSON.c 和一个 cJSON.h,复制粘贴就能用 |
| 高兼容 | 使用 ANSI C (C89) 标准编写,几乎所有平台都能运行 |
| 简单直接 | API 设计直观,专注于解析和生成两件事 |
基本类型
null is created with cJSON_CreateNull,
booleans are created with cJSON_CreateTrue, cJSON_CreateFalse or cJSON_CreateBool,
numbers are created with cJSON_CreateNumber. This will set both valuedouble and valueint. If the number is outside of the range of an integer, INT_MAX or INT_MIN are used for valueint,
strings are created with cJSON_CreateString (copies the string) or with cJSON_CreateStringReference (directly points to the string. This means that valuestring won't be deleted by cJSON_Delete and you are responsible for its lifetime, useful for constants).
另外有Arrays、Objects类型。
由于C语言是强类型语言,任何json中的独立属性对于C实现来说都视为一个独立的对象,然后用指针将这些对象联系起来;具体的话,可以直接阅读cJson.h的函数,都很直观。使用时一定要注意动态内存的释放!尽管json更现代,但是在实际嵌入式平台,如果没有系统内存管理支撑,不建议用c去处理json数据,动态内存分配着实有点令人担心。
克隆代码仓库后在项目文件夹下新建build文件夹,注意这里的编译器要与最终你项目的编译器统一;在build文件夹下执行Cmake生成makefile文件。
cJSON\build>cmake .. -G "Unix Makefiles" -DENABLE_CJSON_TEST=off
也可执行make编译,生成cJson动态库。在build文件夹下生成dll动态库,动态库的特点就是使用的时候不链接的,运行的程序的时候再调用。所以这个dll文件需要放到跟可执行程序相同目录下。其实这个库只有一个cJson.c文件,可以在自己项目中一起编译。
集成步骤
第1步:从 GitHub 下载源码
第2步:把 cJSON.h 和 cJSON.c 扔进你的项目目录
第3步:在代码里加一行
#include "cJSON.h"
如果把文件放在其它目录里,比如 /freeoa/libs/cjson/,那就改成:
#include "/freeoa/libs/cjson/cJSON.h"
在 cJSON 的世界里,不管是对象、数组,还是一个简单的数字,都用同一个结构体 cJSON 来表示。C 语言处理JSON,最怕的就是内存泄漏。
常用的类型与函数对应
| 想创建的对象 | 对应的函数 |
| 对象 {} | cJSON_CreateObject() |
| 数组 [] | cJSON_CreateArray() |
| 字符串 "hello" | cJSON_CreateString("hello") |
| 数字 42 | cJSON_CreateNumber(42) |
| 布尔值 | cJSON_CreateTrue() / cJSON_CreateFalse() |
| 空值 null | cJSON_CreateNull() |
假设要生成这样一段 JSON:
{
"name": "Awesome Scren",
"resolutions": [
{"width": 1280, "height": 720},
{"width": 1920, "height": 1080},
{"width": 3840, "height": 2160}
]
}
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
char* create_monitor_json(void) {
// 1. 创建根对象
cJSON *monitor = cJSON_CreateObject();
// 2. 添加名称字段
cJSON_AddStringToObject(monitor, "name", "Awesome Scren");
// 3. 创建分辨率数组
cJSON *resolutions = cJSON_AddArrayToObject(monitor, "resolutions");
// 4. 往数组里塞数据
int res_data[3][2] = {{1280, 720}, {1920, 1080}, {3840, 2160}};
for (int i = 0; i < 3; i++) {
cJSON *res = cJSON_CreateObject();
cJSON_AddNumberToObject(res, "width", res_data[i][0]);
cJSON_AddNumberToObject(res, "height", res_data[i][1]);
cJSON_AddItemToArray(resolutions, res);
}
// 5. 转成字符串
char *json_str = cJSON_Print(monitor);
// 6. 释放 cJSON 对象
cJSON_Delete(monitor);
return json_str; // 调用者需要 free 这个字符串
}
int main(void) {
char *result = create_monitor_json();
printf("%s\n", result);
free(result); // 别忘了释放!
return 0;
}
构建流程图解

打印函数
• cJSON_Print():生成带缩进的格式化字符串,适合调试和阅读;
• cJSON_PrintUnformatted():生成紧凑字符串,适合网络传输。
注意:cJSON_Print() 返回的字符串是在堆上分配的,用完必须 free() 进行释放,否则内存泄漏。
解析外部的 JSON 字符串
上面演示创建 JSON,那收到一串 JSON 字符串怎么读取呢?
核心函数:cJSON_Parse
cJSON *root = cJSON_Parse(json_string);
这个函数会把字符串解析成一棵 cJSON 对象树。
但有一个关键点:一定要检查返回值是不是 NULL(这也是C中常见的基本操作了)。
cJSON *root = cJSON_Parse(json_string);
if (root == NULL) {
// 解析失败,可能是 JSON 格式不对
const char *error = cJSON_GetErrorPtr();
printf("解析错误:%s\n", error);
return;
}
不检查直接用,程序会直接崩溃。完整的示例参考下文的示例3。
提取数据
1:安全访问对象成员
cJSON *name = cJSON_GetObjectItemCaseSensitive(root, "name");
这个函数有个很棒的特性:自带空指针检查。即使 root 是 NULL,它也不会崩溃,而是返回 NULL。
这意味着你可以安全地链式调用:
// 即使中间某一层是 NULL 也不会崩
cJSON *city = cJSON_GetObjectItemCaseSensitive(
cJSON_GetObjectItemCaseSensitive(root, "address"),
"city"
);
2:类型检查
拿到数据后,别急着用,先问一句"是什么类型":
if (cJSON_IsString(name) && name->valuestring != NULL) {
printf("名称:%s\n", name->valuestring);
}
if (cJSON_IsNumber(price)) {
printf("价格:%.2f\n", price->valuedouble);
}
这是防御性编程的好习惯。万一服务器返回的数据结构和你预期的不一样,至少程序不会直接挂掉。
3:高效遍历数组
cJSON *item = NULL;
cJSON_ArrayForEach(item, resolutions) {
cJSON *width = cJSON_GetObjectItemCaseSensitive(item, "width");
cJSON *height = cJSON_GetObjectItemCaseSensitive(item, "height");
// 处理每个分辨率...
}
cJSON_ArrayForEach 是一个宏,用起来就像 for 循环一样自然。但有两条原则必须要遵守(也是C编程的基本操作):
原则1:谁创建,谁释放
不管是用 cJSON_Parse() 解析出来的,还是用 cJSON_CreateObject() 创建的,只要是顶层对象,用完必须调用 cJSON_Delete()。
// 解析场景
cJSON *root = cJSON_Parse(json_str);
// ... 使用 root ...
cJSON_Delete(root); // 必须释放
// 创建场景
cJSON *obj = cJSON_CreateObject();
// ... 使用 obj ...
cJSON_Delete(obj); // 必须释放
cJSON_Delete() 会递归释放整棵树,包括所有子节点。
原则2:所有权转移
当把一个节点用 cJSON_AddItemToObject() 或 cJSON_AddItemToArray() 添加到另一个对象里时,它的"主动权"就是别人的了。
正确做法:
cJSON *root = cJSON_CreateObject();
cJSON *child = cJSON_CreateString("hello");
cJSON_AddItemToObject(root, "greeting", child);
// child 的所有权已经转移给 root 了
// ... 使用 root ...
cJSON_Delete(root); // 只需要释放 root,child 会被自动释放
错误做法(程序会崩!):
cJSON *root = cJSON_CreateObject();
cJSON *child = cJSON_CreateString("hello");
cJSON_AddItemToObject(root, "greeting", child);
//错误!child 已经属于 root 了
cJSON_Delete(child); // 这里释放了一次
//错误!root 删除时会再释放 child
cJSON_Delete(root); // 重复释放,程序崩溃!
这叫重复释放(double free),是 C 语言内存管理中最经典的坑之一。
也即:
正确操作
1.Parse/Create的顶层对象>必须Delete;
2.子节点 Add 到父节点后>只 Delete 父节点;
3.CJSON Print的返回值→必须 free。
错误操作
1.Add 之后又 Delete 子节点 → 重复释放,崩溃;
2.忘记 Delete 顶层对象 → 内存泄漏;
3.忘记 free 打印的字符串 → 内存泄漏。
核心 API 速查
| 功能 | 函数 |
| 解析 JSON 字符串 | cJSON_Parse() |
| 创建对象/数组 | cJSON_CreateObject() / cJSON_CreateArray() |
| 添加成员 | cJSON_AddStringToObject() / cJSON_AddNumberToObject() |
| 获取成员 | cJSON_GetObjectItemCaseSensitive() |
| 遍历数组 | cJSON_ArrayForEach() |
| 类型检查 | cJSON_IsString() / cJSON_IsNumber() / ... |
| 转成字符串 | cJSON_Print() / cJSON_PrintUnformatted() |
| 释放内存 | cJSON_Delete() |
编码示例1
#include <stdio.h>
#include "cJSON.h"
int main() {
// 创建一个JSON对象
cJSON *root = cJSON_CreateObject();
if (root == NULL) {
return 1;
}
// 在JSON对象中添加一个字符串类型的键值对
cJSON_AddItemToObject(root, "name", cJSON_CreateString("John"));
// 将JSON对象转换为字符串
char *json_string = cJSON_Print(root);
if (json_string == NULL) {
cJSON_Delete(root);
return 1;
}
// 打印JSON字符串
printf("%s\n", json_string);
// 释放内存
cJSON_Delete(root);
free(json_string);
return 0;
}
解码示例
#include <stdio.h>
#include "cJSON.h"
int main() {
const char *json_string = "{\"name\":\"John\"}";
// 解析JSON字符串
cJSON *root = cJSON_Parse(json_string);
if (root == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr!= NULL) {
printf("Error before: %s\n", error_ptr);
}
return 1;
}
// 获取"name"键对应的值
cJSON *name_json = cJSON_GetObjectItem(root, "name");
if (cJSON_IsString(name_json)) {
printf("Name: %s\n", name_json->valuestring);
}
// 释放内存
cJSON_Delete(root);
return 0;
}
编码示例2
封装Json结构并打印我们假定一个json数据结构体:{
"APP_IS_VALID": true,
"REQUEST_UPDATE": false,
"APP_INFO": {
"VERSION": "0.00.001",
"RELEASE_DATE": "2025-05-16",
"SH256": "0123456789abcdef"
},
"CONFIGURATION_TYPE_LIST": ["High","Normal","Low"]
}
build中存放编译相关文件,记得拷贝cJson.dll到build文件夹,在main.c中实现测试。
#include <stdio.h>
#include <stddef.h>
#include "cJson/cJson.h"
#include <stdlib.h>
void demo1(void) {
cJSON *APP_INFO = cJSON_CreateObject();
cJSON_AddItemToObject(APP_INFO, "VERSION", cJSON_CreateString("0.00.001"));
cJSON_AddItemToObject(APP_INFO, "RELEASE_DATE", cJSON_CreateString("2025-05-16"));
cJSON_AddItemToObject(APP_INFO, "SH256", cJSON_CreateString("0123456789abcdef"));
cJSON* CONFIGURATION_TYPE_LIST = cJSON_CreateArray();
cJSON_AddItemToArray(CONFIGURATION_TYPE_LIST, cJSON_CreateString("High"));
cJSON_AddItemToArray(CONFIGURATION_TYPE_LIST, cJSON_CreateString("Normal"));
cJSON_AddItemToArray(CONFIGURATION_TYPE_LIST, cJSON_CreateString("Low"));
cJSON* GLOBAL_CFG = cJSON_CreateObject();
cJSON_AddItemToObject(GLOBAL_CFG, "APP_IS_VALID", cJSON_CreateBool(0));
cJSON_AddItemToObject(GLOBAL_CFG, "REQUEST_UPDATE", cJSON_CreateBool(0));
cJSON_AddItemToObject(GLOBAL_CFG, "APP_INFO", APP_INFO);
cJSON_AddItemToObject(GLOBAL_CFG, "CONFIGURATION_TYPE_LIST", CONFIGURATION_TYPE_LIST);
char *cPrint = cJSON_Print(GLOBAL_CFG);
char *cPrintUnformatted = cJSON_PrintUnformatted(GLOBAL_CFG);
printf("cJSON_Print:\n%s\n", cPrint);
printf("cJSON_PrintUnformatted:\n%s\n", cPrintUnformatted);
// 记得使用cJSON_Print 和 cJSON_PrintUnformatted返回来的字符指针需要free掉内存!
free(cPrint);
free(cPrintUnformatted);
}
int main(void) {
demo1();
}
编译通过后直接执行可以看到结果。
解析Json文件并获取属性
解析时从严谨和安全角度,需要使用结构体中的type类型进行判断,再取数据或者对象;可以使用cJSON_Print函数去获取字符串,也可以使用结构体中的valuestring取值,但要注意的是,使用cJSON_Print函数去获取字符串之后需要free掉获取到的指针,否则会造成内存泄漏。
#include <string.h>
#include <sys/stat.h>
#include <stdbool.h>
void demo2() {
/* open json file*/
FILE *file = NULL;
file = fopen("../cfg.json", "r");
if (file == NULL) {
printf("Open file failed!\n");
return;
}
/* fetch file's size */
struct stat statBuffer;
stat("../cfg.json", &statBuffer);
int fileSize = statBuffer.st_size;
/* malloc memory*/
char *jsonStr = (char *)malloc(sizeof(char) * fileSize + 1);
memset(jsonStr, 0, fileSize + 1);
/* read string */
int size = fread(jsonStr, sizeof(char), fileSize, file);
if (size == 0) {
printf("Read file Failed!\n");
fclose(file);
return;
}
printf("%s\n", jsonStr);
fclose(file);
/* parser string to jsonString*/
cJSON *root = cJSON_Parse(jsonStr);
if (!root) {
printf("Error before: [%s]\n", cJSON_GetErrorPtr());
free(jsonStr);
return;
}
free(jsonStr);
cJSON *item = NULL;
char *v_str = NULL;
double v_double = 0.0;
int v_int = 0;
bool v_bool = false;
/* 获取APP_INFO对象 */
item = cJSON_GetObjectItem(root, "APP_INFO");
if (item != NULL) {
/* 判断是否是新的json对象 */
if (item->type == cJSON_Object) {
cJSON* version = NULL;
/* 获取VERSION对象 */
version = cJSON_GetObjectItem(item, "VERSION");
if (version->type == cJSON_String) {
v_str = version->valuestring;
printf("VERSION = %s\n", v_str);
}
}
}
}
int main(void) {
demo2();
}
在上面的示例中,先从cfg.json文件中一次性读取所有的字符信息,然后将字符一次性转换成jsonString然后解析。这里以获取APP的版本号为例。
编码示例3(检查显示器是否支持 1080P)
#include <stdio.h>
#include "cJSON.h"
// 返回 1 表示支持,0 表示不支持
int supports_full_hd(const char *json_str) {
cJSON *root = cJSON_Parse(json_str);
// 解析失败检查
if (root == NULL) {
printf("JSON 解析失败\n");
return 0;
}
// 获取显示器名称(可选)
cJSON *name = cJSON_GetObjectItemCaseSensitive(root, "name");
if (cJSON_IsString(name)) {
printf("正在检查显示器:%s\n", name->valuestring);
}
// 遍历分辨率数组
cJSON *resolutions = cJSON_GetObjectItemCaseSensitive(root, "resolutions");
cJSON *res = NULL;
cJSON_ArrayForEach(res, resolutions) {
cJSON *w = cJSON_GetObjectItemCaseSensitive(res, "width");
cJSON *h = cJSON_GetObjectItemCaseSensitive(res, "height");
if (cJSON_IsNumber(w) && cJSON_IsNumber(h)) {
if (w->valuedouble == 1920 && h->valuedouble == 1080) {
cJSON_Delete(root); // 别忘了释放
return 1; // 找到了!
}
}
}
cJSON_Delete(root); // 别忘了释放
return 0;
}
最新版本:1.7
v1.7.8于2024年5月发布。
项目主页:https://github.com/DaveGamble/cJSON