cJSON是一个轻量级的JSON解析库,用C语言编写,非常适合在嵌入式系统和资源受限的环境中使用。本教程将详细介绍cJSON库的安装、基本概念、核心函数以及各种使用场景。
1. cJSON简介
cJSON是一个开源的C语言JSON解析库,具有以下特点:
- 轻量级:代码简洁,易于集成
- 单文件:只有一个.c和一个.h文件
- 易于使用:API简单直观
- 高效:解析和生成速度快
2. 安装与配置
2.1 下载cJSON
可以从GitHub仓库下载cJSON:
git clone https://github.com/DaveGamble/cJSON.git
2.2 编译与安装
cd cJSON
mkdir build
cd build
cmake ..
make
sudo make install
2.3 在项目中使用
在项目中包含头文件并链接库:
#include "cjson/cJSON.h"
编译时链接cJSON库:
gcc your_file.c -lcjson -o your_program
3. 基本概念
3.1 cJSON数据结构
cJSON使用链表结构来表示JSON数据:
typedef struct cJSON {
struct cJSON *next, *prev; // 链表指针
struct cJSON *child; // 子节点
int type; // 数据类型
char *valuestring; // 字符串值
int valueint; // 整数值
double valuedouble; // 浮点数值
char *string; // 键名
} cJSON;
3.2 数据类型
cJSON定义了以下数据类型:
cJSON_False: 布尔值falsecJSON_True: 布尔值truecJSON_NULL: null值cJSON_Number: 数字cJSON_String: 字符串cJSON_Array: 数组cJSON_Object: 对象
4. 核心函数详解
4.1 JSON解析函数
cJSON_Parse()
cJSON* cJSON_Parse(const char *value);
- 功能:解析JSON字符串
- 参数:JSON字符串
- 返回:解析后的cJSON对象,失败返回NULL
const char *json_str = "{\"name\":\"张三\",\"age\":25,\"city\":\"北京\"}";
cJSON *root = cJSON_Parse(json_str);
if (root == NULL) {
printf("JSON解析失败\n");
return -1;
}
cJSON_ParseWithOpts()
cJSON* cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
- 功能:带选项的JSON解析
- 参数:
value: JSON字符串return_parse_end: 解析结束位置require_null_terminated: 是否要求字符串以null结尾
const char *json_str = "{\"name\":\"张三\",\"age\":25}";
const char *parse_end = NULL;
cJSON *root = cJSON_ParseWithOpts(json_str, &parse_end, true);
if (root == NULL) {
printf("解析失败,错误位置:%s\n", parse_end);
return -1;
}
4.2 JSON生成函数
cJSON_Print()
char* cJSON_Print(const cJSON *item);
- 功能:将cJSON对象转换为格式化的JSON字符串
- 参数:cJSON对象
- 返回:格式化的JSON字符串
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "name", "张三");
cJSON_AddNumberToObject(root, "age", 25);
char *json_str = cJSON_Print(root);
printf("JSON字符串:%s\n", json_str);
free(json_str); // 记得释放内存
cJSON_PrintUnformatted()
char* cJSON_PrintUnformatted(const cJSON *item);
- 功能:将cJSON对象转换为紧凑的JSON字符串(无格式化)
char *json_str = cJSON_PrintUnformatted(root);
printf("紧凑JSON:%s\n", json_str);
free(json_str);
cJSON_PrintBuffered()
char* cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
- 功能:预分配缓冲区的打印函数
- 参数:
item: cJSON对象prebuffer: 预分配缓冲区大小fmt: 是否格式化
char *json_str = cJSON_PrintBuffered(root, 1024, true);
printf("缓冲区打印:%s\n", json_str);
free(json_str);
4.3 对象创建函数
cJSON_CreateObject()
cJSON* cJSON_CreateObject(void);
- 功能:创建JSON对象
cJSON *obj = cJSON_CreateObject();
if (obj == NULL) {
printf("创建对象失败\n");
return -1;
}
cJSON_CreateArray()
cJSON* cJSON_CreateArray(void);
- 功能:创建JSON数组
cJSON *arr = cJSON_CreateArray();
if (arr == NULL) {
printf("创建数组失败\n");
return -1;
}
4.4 值添加函数
cJSON_AddItemToObject()
cJSON_bool cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
- 功能:向对象添加项
cJSON *root = cJSON_CreateObject();
cJSON *name = cJSON_CreateString("张三");
cJSON_AddItemToObject(root, "name", name);
便捷添加函数
// 添加字符串
cJSON_AddStringToObject(cJSON *object, const char *name, const char *string);
// 添加数字
cJSON_AddNumberToObject(cJSON *object, const char *name, double number);
// 添加布尔值
cJSON_AddBoolToObject(cJSON *object, const char *name, int b);
// 添加null
cJSON_AddNullToObject(cJSON *object, const char *name);
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "name", "张三");
cJSON_AddNumberToObject(root, "age", 25.5);
cJSON_AddBoolToObject(root, "married", 1);
cJSON_AddNullToObject(root, "spouse");
数组操作函数
// 向数组添加项
cJSON_AddItemToArray(cJSON *array, cJSON *item);
// 从数组添加项并返回
cJSON* cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
// 向对象添加数组
cJSON* cJSON_AddArrayToObject(cJSON *object, const char *name);
cJSON *root = cJSON_CreateObject();
cJSON *scores = cJSON_AddArrayToObject(root, "scores");
cJSON *score1 = cJSON_CreateNumber(85);
cJSON *score2 = cJSON_CreateNumber(92);
cJSON *score3 = cJSON_CreateNumber(78);
cJSON_AddItemToArray(scores, score1);
cJSON_AddItemToArray(scores, score2);
cJSON_AddItemToArray(scores, score3);
4.5 查找函数
cJSON_GetObjectItem()
cJSON* cJSON_GetObjectItem(const cJSON * const object, const char * const string);
- 功能:根据键名获取对象中的项
cJSON *name_item = cJSON_GetObjectItem(root, "name");
if (cJSON_IsString(name_item)) {
printf("姓名:%s\n", name_item->valuestring);
}
cJSON_GetObjectItemCaseSensitive()
cJSON* cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
- 功能:大小写敏感的键名查找
cJSON_GetArraySize()
int cJSON_GetArraySize(const cJSON *array);
- 功能:获取数组大小
cJSON *scores = cJSON_GetObjectItem(root, "scores");
int size = cJSON_GetArraySize(scores);
printf("成绩数量:%d\n", size);
cJSON_GetArrayItem()
cJSON* cJSON_GetArrayItem(const cJSON *array, int index);
- 功能:根据索引获取数组项
for (int i = 0; i < size; i++) {
cJSON *score = cJSON_GetArrayItem(scores, i);
if (cJSON_IsNumber(score)) {
printf("第%d个成绩:%g\n", i + 1, score->valuedouble);
}
}
4.6 类型检查函数
cJSON_IsInvalid(const cJSON * const item)
cJSON_IsFalse(const cJSON * const item)
cJSON_IsTrue(const cJSON * const item)
cJSON_IsBool(const cJSON * const item)
cJSON_IsNull(const cJSON * const item)
cJSON_IsNumber(const cJSON * const item)
cJSON_IsString(const cJSON * const item)
cJSON_IsArray(const cJSON * const item)
cJSON_IsObject(const cJSON * const item)
cJSON_IsRaw(const cJSON * const item)
cJSON *item = cJSON_GetObjectItem(root, "age");
if (cJSON_IsNumber(item)) {
printf("年龄:%g\n", item->valuedouble);
}
4.7 内存管理函数
cJSON_Delete()
void cJSON_Delete(cJSON *item);
- 功能:释放cJSON对象及其所有子节点的内存
cJSON_Delete(root); // 释放整个JSON树
cJSON_DetachItemFromArray()
cJSON* cJSON_DetachItemFromArray(cJSON *array, int which);
- 功能:从数组中分离项(不释放内存)
cJSON *item = cJSON_DetachItemFromArray(array, 0);
// item现在可以被重新使用或单独删除
cJSON_Delete(item);
cJSON_DetachItemFromObject()
cJSON* cJSON_DetachItemFromObject(cJSON *object, const char *string);
- 功能:从对象中分离项
cJSON *name_item = cJSON_DetachItemFromObject(root, "name");
if (name_item != NULL) {
printf("分离的姓名:%s\n", name_item->valuestring);
cJSON_Delete(name_item);
}
5. 实际应用示例
5.1 创建复杂JSON
#include <stdio.h>
#include <stdlib.h>
#include "cjson/cJSON.h"
int main() {
// 创建根对象
cJSON *root = cJSON_CreateObject();
if (root == NULL) {
printf("创建根对象失败\n");
return -1;
}
// 添加基本属性
cJSON_AddStringToObject(root, "name", "张三");
cJSON_AddNumberToObject(root, "age", 25);
cJSON_AddBoolToObject(root, "married", 0);
cJSON_AddNullToObject(root, "spouse");
// 创建地址对象
cJSON *address = cJSON_CreateObject();
cJSON_AddStringToObject(address, "street", "长安街1号");
cJSON_AddStringToObject(address, "city", "北京");
cJSON_AddStringToObject(address, "zipcode", "100000");
cJSON_AddItemToObject(root, "address", address);
// 创建电话数组
cJSON *phones = cJSON_CreateArray();
cJSON_AddItemToArray(phones, cJSON_CreateString("13800138000"));
cJSON_AddItemToArray(phones, cJSON_CreateString("010-12345678"));
cJSON_AddItemToObject(root, "phones", phones);
// 创建技能数组
cJSON *skills = cJSON_CreateArray();
cJSON_AddItemToArray(skills, cJSON_CreateString("C语言"));
cJSON_AddItemToArray(skills, cJSON_CreateString("Python"));
cJSON_AddItemToArray(skills, cJSON_CreateString("Java"));
cJSON_AddItemToObject(root, "skills", skills);
// 打印JSON
char *json_str = cJSON_Print(root);
printf("生成的JSON:\n%s\n", json_str);
// 释放内存
free(json_str);
cJSON_Delete(root);
return 0;
}
5.2 解析JSON并提取数据
#include <stdio.h>
#include <stdlib.h>
#include "cjson/cJSON.h"
int main() {
const char *json_str =
"{"
"\"name\":\"张三\","
"\"age\":25,"
"\"married\":false,"
"\"address\":{"
"\"street\":\"长安街1号\","
"\"city\":\"北京\","
"\"zipcode\":\"100000\""
"},"
"\"phones\":[\"13800138000\",\"010-12345678\"],"
"\"skills\":[\"C语言\",\"Python\",\"Java\"]"
"}";
// 解析JSON
cJSON *root = cJSON_Parse(json_str);
if (root == NULL) {
printf("JSON解析失败\n");
return -1;
}
// 提取基本属性
cJSON *name = cJSON_GetObjectItem(root, "name");
cJSON *age = cJSON_GetObjectItem(root, "age");
cJSON *married = cJSON_GetObjectItem(root, "married");
if (cJSON_IsString(name)) {
printf("姓名:%s\n", name->valuestring);
}
if (cJSON_IsNumber(age)) {
printf("年龄:%d\n", age->valueint);
}
if (cJSON_IsBool(married)) {
printf("婚姻状况:%s\n", cJSON_IsTrue(married) ? "已婚" : "未婚");
}
// 提取地址对象
cJSON *address = cJSON_GetObjectItem(root, "address");
if (cJSON_IsObject(address)) {
cJSON *street = cJSON_GetObjectItem(address, "street");
cJSON *city = cJSON_GetObjectItem(address, "city");
cJSON *zipcode = cJSON_GetObjectItem(address, "zipcode");
if (cJSON_IsString(street)) {
printf("街道:%s\n", street->valuestring);
}
if (cJSON_IsString(city)) {
printf("城市:%s\n", city->valuestring);
}
if (cJSON_IsString(zipcode)) {
printf("邮编:%s\n", zipcode->valuestring);
}
}
// 提取电话数组
cJSON *phones = cJSON_GetObjectItem(root, "phones");
if (cJSON_IsArray(phones)) {
int phone_count = cJSON_GetArraySize(phones);
printf("电话数量:%d\n", phone_count);
for (int i = 0; i < phone_count; i++) {
cJSON *phone = cJSON_GetArrayItem(phones, i);
if (cJSON_IsString(phone)) {
printf("电话%d:%s\n", i + 1, phone->valuestring);
}
}
}
// 提取技能数组
cJSON *skills = cJSON_GetObjectItem(root, "skills");
if (cJSON_IsArray(skills)) {
int skill_count = cJSON_GetArraySize(skills);
printf("技能数量:%d\n", skill_count);
for (int i = 0; i < skill_count; i++) {
cJSON *skill = cJSON_GetArrayItem(skills, i);
if (cJSON_IsString(skill)) {
printf("技能%d:%s\n", i + 1, skill->valuestring);
}
}
}
// 释放内存
cJSON_Delete(root);
return 0;
}
5.3 JSON数组操作
#include <stdio.h>
#include <stdlib.h>
#include "cjson/cJSON.h"
int main() {
// 创建数组
cJSON *array = cJSON_CreateArray();
if (array == NULL) {
printf("创建数组失败\n");
return -1;
}
// 添加不同类型的元素
cJSON_AddItemToArray(array, cJSON_CreateString("苹果"));
cJSON_AddItemToArray(array, cJSON_CreateString("香蕉"));
cJSON_AddItemToArray(array, cJSON_CreateString("橙子"));
cJSON_AddItemToArray(array, cJSON_CreateNumber(1));
cJSON_AddItemToArray(array, cJSON_CreateNumber(2));
cJSON_AddItemToArray(array, cJSON_CreateNumber(3));
cJSON_AddItemToArray(array, cJSON_CreateTrue());
cJSON_AddItemToArray(array, cJSON_CreateFalse());
cJSON_AddItemToArray(array, cJSON_CreateNull());
// 创建嵌套对象
cJSON *obj1 = cJSON_CreateObject();
cJSON_AddStringToObject(obj1, "name", "产品A");
cJSON_AddNumberToObject(obj1, "price", 100.5);
cJSON_AddItemToArray(array, obj1);
cJSON *obj2 = cJSON_CreateObject();
cJSON_AddStringToObject(obj2, "name", "产品B");
cJSON_AddNumberToObject(obj2, "price", 200.8);
cJSON_AddItemToArray(array, obj2);
// 打印数组
char *json_str = cJSON_Print(array);
printf("数组内容:\n%s\n", json_str);
// 遍历数组
int size = cJSON_GetArraySize(array);
printf("\n遍历数组内容:\n");
for (int i = 0; i < size; i++) {
cJSON *item = cJSON_GetArrayItem(array, i);
printf("索引%d: ", i);
if (cJSON_IsString(item)) {
printf("字符串: %s\n", item->valuestring);
} else if (cJSON_IsNumber(item)) {
printf("数字: %g\n", item->valuedouble);
} else if (cJSON_IsBool(item)) {
printf("布尔值: %s\n", cJSON_IsTrue(item) ? "true" : "false");
} else if (cJSON_IsNull(item)) {
printf("null值\n");
} else if (cJSON_IsObject(item)) {
printf("对象: ");
cJSON *name = cJSON_GetObjectItem(item, "name");
cJSON *price = cJSON_GetObjectItem(item, "price");
if (name && cJSON_IsString(name)) {
printf("名称=%s, ", name->valuestring);
}
if (price && cJSON_IsNumber(price)) {
printf("价格=%g", price->valuedouble);
}
printf("\n");
}
}
// 释放内存
free(json_str);
cJSON_Delete(array);
return 0;
}
5.4 错误处理和调试
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cjson/cJSON.h"
// 自定义错误处理函数
void print_json_error(const char *json_str, const char *error_msg) {
printf("JSON错误:%s\n", error_msg);
printf("JSON字符串:%s\n", json_str);
// 找到错误位置(简化版本)
const char *parse_end = NULL;
cJSON *root = cJSON_ParseWithOpts(json_str, &parse_end, false);
if (root == NULL && parse_end != NULL) {
printf("错误位置:");
for (int i = 0; i < strlen(json_str) && &json_str[i] != parse_end; i++) {
if (json_str[i] == '\n') {
printf("\\n");
} else if (json_str[i] == '\t') {
printf("\\t");
} else {
printf("%c", json_str[i]);
}
}
printf(" <-- 这里\n");
}
if (root) cJSON_Delete(root);
}
int main() {
// 测试正确的JSON
const char *valid_json = "{\"name\":\"张三\",\"age\":25}";
cJSON *root = cJSON_Parse(valid_json);
if (root != NULL) {
printf("正确JSON解析成功\n");
cJSON_Delete(root);
}
// 测试错误的JSON
const char *invalid_json = "{\"name\":\"张三\",\"age\":25"; // 缺少右括号
root = cJSON_Parse(invalid_json);
if (root == NULL) {
print_json_error(invalid_json, "JSON格式错误");
}
// 更复杂的错误JSON
const char *complex_invalid =
"{"
"\"users\":["
"{\"name\":\"张三\",\"age\":25},"
"{\"name\":\"李四\",\"age\":\"二十五\"}" // 年龄应该是数字
"]"
"}";
root = cJSON_Parse(complex_invalid);
if (root == NULL) {
print_json_error(complex_invalid, "复杂JSON解析失败");
} else {
// 检查数据类型
cJSON *users = cJSON_GetObjectItem(root, "users");
if (cJSON_IsArray(users)) {
cJSON *user = cJSON_GetArrayItem(users, 1);
if (user) {
cJSON *age = cJSON_GetObjectItem(user, "age");
if (age && !cJSON_IsNumber(age)) {
printf("警告:年龄字段不是数字类型\n");
}
}
}
cJSON_Delete(root);
}
return 0;
}
6. 高级功能
6.1 JSON引用
// 创建引用,避免重复数据
cJSON *original = cJSON_CreateString("共享数据");
cJSON *reference = cJSON_CreateReference(original);
// 将引用添加到多个位置
cJSON *root1 = cJSON_CreateObject();
cJSON *root2 = cJSON_CreateObject();
cJSON_AddItemToObject(root1, "data", reference);
cJSON_AddItemToObject(root2, "data", cJSON_CreateReference(original));
// 注意:删除时要小心,避免重复释放
cJSON_Delete(root1);
cJSON_Delete(root2);
cJSON_Delete(original); // 最后删除原始数据
6.2 自定义内存管理
// 设置自定义内存分配函数
void* custom_malloc(size_t size) {
printf("分配内存:%zu字节\n", size);
return malloc(size);
}
void custom_free(void *ptr) {
printf("释放内存\n");
free(ptr);
}
// 在解析前设置
cJSON_Hooks hooks;
hooks.malloc_fn = custom_malloc;
hooks.free_fn = custom_free;
cJSON_InitHooks(&hooks);
// 现在所有的内存分配都会使用自定义函数
cJSON *root = cJSON_CreateObject();
// ... 使用cJSON ...
cJSON_Delete(root);
6.3 流式解析
// 对于大JSON文件,可以使用流式解析
int stream_parse(const char *filename) {
FILE *file = fopen(filename, "r");
if (!file) {
printf("无法打开文件:%s\n", filename);
return -1;
}
// 分配缓冲区
char buffer[4096];
cJSON *root = NULL;
const char *parse_end = NULL;
while (fgets(buffer, sizeof(buffer), file)) {
// 去除换行符
buffer[strcspn(buffer, "\n")] = 0;
// 解析当前行
cJSON *partial = cJSON_ParseWithOpts(buffer, &parse_end, false);
if (partial) {
if (root == NULL) {
root = partial;
} else {
// 合并到根对象(需要根据具体结构实现)
// 这里只是一个示例
cJSON_Delete(partial);
}
} else {
printf("解析失败:%s\n", buffer);
printf("错误位置:%s\n", parse_end ? parse_end : "未知");
}
}
fclose(file);
if (root) {
// 处理解析结果
char *json_str = cJSON_Print(root);
printf("解析结果:%s\n", json_str);
free(json_str);
cJSON_Delete(root);
}
return 0;
}
7. 最佳实践
7.1 内存管理
- 总是使用
cJSON_Delete()释放内存 - 确保每个
cJSON_Create*都有对应的cJSON_Delete - 避免内存泄漏
// 正确的内存管理
cJSON *root = cJSON_CreateObject();
if (root) {
// ... 使用JSON ...
cJSON_Delete(root); // 必须释放
}
7.2 错误处理
- 总是检查函数返回值
- 处理解析错误
- 验证数据类型
cJSON *root = cJSON_Parse(json_str);
if (root == NULL) {
printf("JSON解析失败\n");
return -1;
}
cJSON *name = cJSON_GetObjectItem(root, "name");
if (name == NULL || !cJSON_IsString(name)) {
printf("缺少或无效的name字段\n");
cJSON_Delete(root);
return -1;
}
7.3 性能优化
- 对于频繁使用的JSON结构,考虑缓存解析结果
- 使用
cJSON_PrintUnformatted()减少输出大小 - 避免不必要的JSON创建和解析
8. 常见问题解答
Q1: 如何处理中文字符?
A: cJSON支持UTF-8编码,确保你的JSON字符串是UTF-8编码即可。
Q2: 如何处理大文件?
A: 对于大JSON文件,考虑分块解析或使用流式处理。
Q3: 如何避免内存泄漏?
A: 确保每个创建的cJSON对象都有对应的删除操作,可以使用RAII模式或智能指针(在C++中)。
Q4: cJSON线程安全吗?
A: cJSON本身不是线程安全的,如果在多线程环境中使用,需要自行加锁。
1360

被折叠的 条评论
为什么被折叠?



