cJSON高级特性详解:数组与对象操作全攻略
【免费下载链接】cJSON Ultralightweight JSON parser in ANSI C 项目地址: https://gitcode.com/gh_mirrors/cj/cJSON
引言:JSON处理的痛点与解决方案
你是否在嵌入式开发中遇到过JSON解析效率低下的问题?是否为复杂数据结构的序列化与反序列化而烦恼?cJSON作为一款超轻量级的ANSI C JSON解析器,以其极致的性能和微小的内存占用,成为嵌入式系统和资源受限环境的理想选择。本文将深入剖析cJSON中数组与对象的高级操作技巧,带你掌握从基础创建到复杂嵌套结构处理的全流程解决方案。
读完本文后,你将能够:
- 熟练运用cJSON进行数组的创建、遍历与修改
- 掌握对象的高级操作技巧,包括深拷贝与比较
- 处理复杂嵌套JSON结构,避免内存泄漏
- 优化JSON数据处理性能,应对资源受限环境
cJSON核心数据结构解析
cJSON结构体定义
cJSON的核心是cJSON结构体,它定义了JSON数据的基本单元:
typedef struct cJSON {
struct cJSON *next; /* 下一个节点 */
struct cJSON *prev; /* 上一个节点 */
struct cJSON *child; /* 子节点,用于数组和对象 */
int type; /* 数据类型 */
char *valuestring; /* 字符串值 */
int valueint; /* 整数值 */
double valuedouble; /* 浮点数值 */
char *string; /* 对象键名 */
} cJSON;
数据类型常量
cJSON定义了多种数据类型常量,用于标识JSON值的类型:
| 类型常量 | 说明 |
|---|---|
cJSON_False | 布尔值false |
cJSON_True | 布尔值true |
cJSON_NULL | NULL值 |
cJSON_Number | 数字 |
cJSON_String | 字符串 |
cJSON_Array | 数组 |
cJSON_Object | 对象 |
cJSON_Raw | 原始JSON |
数组操作全攻略
数组创建与初始化
cJSON提供了多种创建数组的方法,适用于不同场景:
1. 基础数组创建
// 创建空数组
cJSON *array = cJSON_CreateArray();
if (array == NULL) {
// 处理创建失败
goto error;
}
// 向数组添加元素
cJSON *item = cJSON_CreateNumber(123);
if (item == NULL) {
// 处理创建失败
goto error;
}
cJSON_AddItemToArray(array, item);
2. 批量创建数组
对于已知元素的数组,可以使用批量创建函数提高效率:
// 创建整数数组
int numbers[] = {1, 2, 3, 4, 5};
cJSON *int_array = cJSON_CreateIntArray(numbers, 5);
// 创建字符串数组
const char *strings[] = {"apple", "banana", "cherry"};
cJSON *str_array = cJSON_CreateStringArray(strings, 3);
3. 嵌套数组创建
创建多维数组需要结合数组创建和添加元素的操作:
// 创建二维数组 [[1,2],[3,4],[5,6]]
cJSON *matrix = cJSON_CreateArray();
if (matrix == NULL) goto error;
int row1[] = {1, 2};
cJSON *array1 = cJSON_CreateIntArray(row1, 2);
if (array1 == NULL) goto error;
cJSON_AddItemToArray(matrix, array1);
int row2[] = {3, 4};
cJSON *array2 = cJSON_CreateIntArray(row2, 2);
if (array2 == NULL) goto error;
cJSON_AddItemToArray(matrix, array2);
int row3[] = {5, 6};
cJSON *array3 = cJSON_CreateIntArray(row3, 2);
if (array3 == NULL) goto error;
cJSON_AddItemToArray(matrix, array3);
数组元素访问与遍历
1. 获取数组大小与元素
// 获取数组大小
int size = cJSON_GetArraySize(array);
// 通过索引访问元素
cJSON *item = cJSON_GetArrayItem(array, 0); // 获取第一个元素
if (item == NULL || !cJSON_IsNumber(item)) {
// 处理错误
}
double value = item->valuedouble;
2. 使用迭代器遍历数组
cJSON提供了便捷的宏定义用于数组遍历:
cJSON *current = NULL;
int index = 0;
cJSON_ArrayForEach(current, array) {
printf("Element %d: ", index);
if (cJSON_IsNumber(current)) {
printf("%lf\n", current->valuedouble);
} else if (cJSON_IsString(current)) {
printf("%s\n", current->valuestring);
}
index++;
}
3. 递归遍历嵌套数组
对于复杂的嵌套数组,需要使用递归方式遍历:
void traverse_array(const cJSON *array) {
if (array == NULL || !cJSON_IsArray(array)) return;
cJSON *item = NULL;
cJSON_ArrayForEach(item, array) {
if (cJSON_IsArray(item)) {
printf("[\n");
traverse_array(item); // 递归遍历子数组
printf("]\n");
} else if (cJSON_IsNumber(item)) {
printf("%lf, ", item->valuedouble);
} else if (cJSON_IsString(item)) {
printf("\"%s\", ", item->valuestring);
}
}
}
数组元素修改与删除
1. 添加元素到数组
// 在数组末尾添加元素
cJSON *new_item = cJSON_CreateString("new element");
if (new_item == NULL) goto error;
cJSON_AddItemToArray(array, new_item);
// 在指定位置插入元素
cJSON *insert_item = cJSON_CreateNumber(999);
if (insert_item == NULL) goto error;
cJSON_InsertItemInArray(array, 2, insert_item); // 在索引2处插入
2. 替换数组元素
// 替换指定索引的元素
cJSON *new_item = cJSON_CreateNumber(100);
if (new_item == NULL) goto error;
// 先获取旧元素
cJSON *old_item = cJSON_GetArrayItem(array, 0);
if (old_item != NULL) {
// 替换元素
cJSON_ReplaceItemInArray(array, 0, new_item);
// 注意:替换后不需要手动删除旧元素,cJSON会自动处理
}
3. 删除数组元素
// 方法1: 分离并删除元素
cJSON *removed = cJSON_DetachItemFromArray(array, 1);
if (removed != NULL) {
cJSON_Delete(removed); // 必须手动删除分离的元素
}
// 方法2: 直接删除元素
cJSON_DeleteItemFromArray(array, 2); // 直接删除索引2的元素
对象操作高级技巧
对象创建与属性管理
1. 基础对象创建
// 创建空对象
cJSON *object = cJSON_CreateObject();
if (object == NULL) goto error;
// 添加属性
cJSON_AddStringToObject(object, "name", "cJSON");
cJSON_AddNumberToObject(object, "version", 1.7);
cJSON_AddBoolToObject(object, "stable", cJSON_True);
2. 使用便捷函数创建对象
cJSON提供了一系列便捷函数,简化对象创建过程:
cJSON *user = cJSON_CreateObject();
if (user == NULL) goto error;
// 使用便捷函数添加属性,失败时直接处理
if (cJSON_AddStringToObject(user, "username", "john_doe") == NULL) goto error;
if (cJSON_AddNumberToObject(user, "age", 30) == NULL) goto error;
if (cJSON_AddBoolToObject(user, "verified", cJSON_True) == NULL) goto error;
// 添加嵌套对象
cJSON *address = cJSON_AddObjectToObject(user, "address");
if (address == NULL) goto error;
if (cJSON_AddStringToObject(address, "street", "123 Main St") == NULL) goto error;
if (cJSON_AddStringToObject(address, "city", "New York") == NULL) goto error;
3. 从结构体创建对象
在实际应用中,经常需要将结构体转换为JSON对象:
typedef struct {
const char *name;
int age;
float score;
int hobbies[3];
} Student;
cJSON *student_to_json(const Student *stu) {
cJSON *obj = cJSON_CreateObject();
if (obj == NULL) return NULL;
// 添加基本类型属性
if (cJSON_AddStringToObject(obj, "name", stu->name) == NULL) goto error;
if (cJSON_AddNumberToObject(obj, "age", stu->age) == NULL) goto error;
if (cJSON_AddNumberToObject(obj, "score", stu->score) == NULL) goto error;
// 添加数组属性
cJSON *hobbies = cJSON_CreateIntArray(stu->hobbies, 3);
if (hobbies == NULL) goto error;
cJSON_AddItemToObject(obj, "hobbies", hobbies);
return obj;
error:
cJSON_Delete(obj);
return NULL;
}
对象属性访问与遍历
1. 获取对象属性
// 获取属性(不区分大小写)
cJSON *name = cJSON_GetObjectItem(object, "Name");
// 获取属性(区分大小写)
cJSON *version = cJSON_GetObjectItemCaseSensitive(object, "version");
// 检查属性是否存在
if (cJSON_HasObjectItem(object, "stable")) {
printf("Object has 'stable' property\n");
}
// 安全获取字符串值
const char *name_str = cJSON_GetStringValue(cJSON_GetObjectItem(object, "name"));
// 安全获取数字值
double version_num = cJSON_GetNumberValue(cJSON_GetObjectItem(object, "version"));
2. 遍历对象属性
// 遍历对象的所有属性
cJSON *current = NULL;
cJSON_ArrayForEach(current, object) {
// current->string 是属性名
// current 是属性值
printf("Key: %s, Type: %d\n", current->string, current->type);
if (cJSON_IsString(current)) {
printf("Value: %s\n", current->valuestring);
} else if (cJSON_IsNumber(current)) {
printf("Value: %lf\n", current->valuedouble);
}
}
对象高级操作
1. 对象深拷贝与比较
// 创建对象副本(深拷贝)
cJSON *object_copy = cJSON_Duplicate(object, cJSON_True); // 第二个参数为是否递归拷贝
if (object_copy == NULL) goto error;
// 比较两个对象
cJSON_bool equal = cJSON_Compare(object, object_copy, cJSON_True); // 第三个参数为是否区分大小写比较键名
if (equal) {
printf("Objects are equal\n");
} else {
printf("Objects are different\n");
}
2. 对象属性修改与删除
// 替换对象属性
cJSON *new_version = cJSON_CreateNumber(2.0);
if (new_version == NULL) goto error;
cJSON_ReplaceItemInObject(object, "version", new_version);
// 删除对象属性
cJSON_DeleteItemFromObject(object, "stable"); // 删除"stable"属性
// 分离并删除属性
cJSON *detached = cJSON_DetachItemFromObject(object, "temp");
if (detached != NULL) {
cJSON_Delete(detached);
}
3. 合并对象
cJSON没有直接提供合并对象的函数,但可以通过遍历实现:
cJSON_bool merge_objects(cJSON *dest, const cJSON *src) {
if (dest == NULL || src == NULL || !cJSON_IsObject(dest) || !cJSON_IsObject(src)) {
return cJSON_False;
}
cJSON *current = NULL;
cJSON_ArrayForEach(current, src) {
// 创建属性副本
cJSON *copy = cJSON_Duplicate(current, cJSON_True);
if (copy == NULL) return cJSON_False;
// 检查目标对象中是否已有该属性
cJSON *existing = cJSON_GetObjectItemCaseSensitive(dest, current->string);
if (existing != NULL) {
// 替换已有属性
cJSON_ReplaceItemInObject(dest, current->string, copy);
} else {
// 添加新属性
cJSON_AddItemToObject(dest, current->string, copy);
}
}
return cJSON_True;
}
复杂嵌套结构处理
嵌套JSON结构创建
创建包含数组和对象的复杂嵌套结构是JSON处理中的常见需求:
// 创建一个复杂的嵌套JSON结构
cJSON *create_complex_json(void) {
// 创建根对象
cJSON *root = cJSON_CreateObject();
if (root == NULL) goto error;
// 添加基本属性
if (cJSON_AddStringToObject(root, "title", "cJSON Tutorial") == NULL) goto error;
if (cJSON_AddNumberToObject(root, "level", 3) == NULL) goto error;
// 添加作者对象
cJSON *author = cJSON_AddObjectToObject(root, "author");
if (author == NULL) goto error;
if (cJSON_AddStringToObject(author, "name", "John Doe") == NULL) goto error;
if (cJSON_AddNumberToObject(author, "experience", 10) == NULL) goto error;
// 添加标签数组
const char *tags[] = {"C", "JSON", "Parsing"};
cJSON *tag_array = cJSON_CreateStringArray(tags, 3);
if (tag_array == NULL) goto error;
cJSON_AddItemToObject(root, "tags", tag_array);
// 添加示例数组(包含对象)
cJSON *examples = cJSON_AddArrayToObject(root, "examples");
if (examples == NULL) goto error;
// 第一个示例
cJSON *example1 = cJSON_CreateObject();
if (example1 == NULL) goto error;
if (cJSON_AddStringToObject(example1, "name", "Basic Object") == NULL) goto error;
if (cJSON_AddStringToObject(example1, "description", "Create a simple JSON object") == NULL) goto error;
cJSON_AddItemToArray(examples, example1);
// 第二个示例
cJSON *example2 = cJSON_CreateObject();
if (example2 == NULL) goto error;
if (cJSON_AddStringToObject(example2, "name", "Nested Array") == NULL) goto error;
if (cJSON_AddStringToObject(example2, "description", "Create a nested JSON array") == NULL) goto error;
cJSON_AddItemToArray(examples, example2);
return root;
error:
cJSON_Delete(root);
return NULL;
}
解析复杂JSON结构
解析嵌套JSON需要结合对象和数组的操作:
// 解析复杂JSON结构示例
void parse_complex_json(const char *json_str) {
// 解析JSON字符串
cJSON *root = cJSON_Parse(json_str);
if (root == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL) {
fprintf(stderr, "JSON parse error: %s\n", error_ptr);
}
return;
}
// 获取基本属性
const cJSON *title = cJSON_GetObjectItemCaseSensitive(root, "title");
if (cJSON_IsString(title) && title->valuestring != NULL) {
printf("Title: %s\n", title->valuestring);
}
// 获取作者信息
const cJSON *author = cJSON_GetObjectItemCaseSensitive(root, "author");
if (cJSON_IsObject(author)) {
const cJSON *name = cJSON_GetObjectItemCaseSensitive(author, "name");
const cJSON *experience = cJSON_GetObjectItemCaseSensitive(author, "experience");
if (cJSON_IsString(name) && name->valuestring != NULL &&
cJSON_IsNumber(experience)) {
printf("Author: %s, Experience: %.0f years\n",
name->valuestring, experience->valuedouble);
}
}
// 获取标签数组
const cJSON *tags = cJSON_GetObjectItemCaseSensitive(root, "tags");
if (cJSON_IsArray(tags)) {
printf("Tags: ");
cJSON *tag = NULL;
cJSON_ArrayForEach(tag, tags) {
if (cJSON_IsString(tag) && tag->valuestring != NULL) {
printf("%s ", tag->valuestring);
}
}
printf("\n");
}
// 获取示例数组
const cJSON *examples = cJSON_GetObjectItemCaseSensitive(root, "examples");
if (cJSON_IsArray(examples)) {
int example_count = cJSON_GetArraySize(examples);
printf("Found %d examples:\n", example_count);
cJSON *example = NULL;
cJSON_ArrayForEach(example, examples) {
const cJSON *name = cJSON_GetObjectItemCaseSensitive(example, "name");
const cJSON *desc = cJSON_GetObjectItemCaseSensitive(example, "description");
if (cJSON_IsString(name) && name->valuestring != NULL &&
cJSON_IsString(desc) && desc->valuestring != NULL) {
printf("- %s: %s\n", name->valuestring, desc->valuestring);
}
}
}
// 释放资源
cJSON_Delete(root);
}
性能优化与内存管理
内存管理最佳实践
cJSON需要手动管理内存,错误的内存管理会导致内存泄漏或程序崩溃:
// 正确的内存管理示例
void proper_memory_management(void) {
// 创建对象
cJSON *object = cJSON_CreateObject();
if (object == NULL) return;
// 添加属性
cJSON *array = cJSON_CreateArray();
if (array == NULL) {
cJSON_Delete(object); // 创建失败时删除已创建的对象
return;
}
cJSON_AddItemToObject(object, "data", array);
// 添加数组元素
for (int i = 0; i < 5; i++) {
cJSON *item = cJSON_CreateNumber(i);
if (item == NULL) {
cJSON_Delete(object); // 创建失败时删除整个对象树
return;
}
cJSON_AddItemToArray(array, item);
}
// 使用对象...
char *json_str = cJSON_Print(object);
if (json_str != NULL) {
printf("%s\n", json_str);
free(json_str); // 必须释放打印出的JSON字符串
}
// 释放根对象(会递归释放所有子对象)
cJSON_Delete(object);
}
高效JSON生成与打印
// 高效打印JSON的方法
void efficient_json_printing(const cJSON *object) {
// 方法1: 标准打印(带格式)
char *formatted = cJSON_Print(object);
if (formatted != NULL) {
printf("Formatted JSON:\n%s\n", formatted);
free(formatted);
}
// 方法2: 无格式打印(更高效)
char *unformatted = cJSON_PrintUnformatted(object);
if (unformatted != NULL) {
printf("Unformatted JSON:\n%s\n", unformatted);
free(unformatted);
}
// 方法3: 预分配缓冲区打印(最高效)
// 先估算所需大小
char *temp = cJSON_Print(object);
if (temp == NULL) return;
int len = strlen(temp) + 5; // 加5个字节防止估算不足
char *buffer = (char*)malloc(len);
if (buffer != NULL) {
if (cJSON_PrintPreallocated(object, buffer, len, cJSON_True)) {
printf("Preallocated JSON:\n%s\n", buffer);
}
free(buffer);
}
free(temp);
}
内存使用优化技巧
在资源受限环境中,需要特别注意内存优化:
// 内存优化示例
void optimize_memory_usage(void) {
// 1. 使用引用而非复制字符串
const char *static_string = "This is a static string";
cJSON *str_ref = cJSON_CreateStringReference(static_string);
// 注意:当使用StringReference时,cJSON不会释放该字符串
// 2. 使用对象/数组引用
cJSON *shared_array = cJSON_CreateIntArray((int[]){1,2,3}, 3);
cJSON *obj1 = cJSON_CreateObject();
cJSON *obj2 = cJSON_CreateObject();
// 添加引用而非复制
cJSON_AddItemToObject(obj1, "shared", cJSON_CreateArrayReference(shared_array));
cJSON_AddItemToObject(obj2, "shared", cJSON_CreateArrayReference(shared_array));
// 注意:引用的对象需要手动管理生命周期,不能被引用它的对象删除
// 清理
cJSON_Delete(str_ref);
cJSON_Delete(obj1);
cJSON_Delete(obj2);
cJSON_Delete(shared_array); // 最后删除被引用的对象
}
实战案例:配置文件解析器
下面实现一个完整的JSON配置文件解析器,展示cJSON数组与对象操作的综合应用:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"
// 配置结构体
typedef struct {
char *app_name;
int version[3];
int port;
char **allowed_ips;
int allowed_ips_count;
struct {
char *host;
int timeout;
int retries;
} database;
} AppConfig;
// 读取文件内容
char *read_file(const char *filename) {
FILE *file = fopen(filename, "rb");
if (file == NULL) return NULL;
fseek(file, 0, SEEK_END);
long length = ftell(file);
fseek(file, 0, SEEK_SET);
char *buffer = (char*)malloc(length + 1);
if (buffer == NULL) {
fclose(file);
return NULL;
}
size_t read = fread(buffer, 1, length, file);
fclose(file);
if (read != length) {
free(buffer);
return NULL;
}
buffer[length] = '\0';
return buffer;
}
// 解析配置文件
AppConfig *parse_config(const char *filename) {
// 分配配置结构体
AppConfig *config = (AppConfig*)calloc(1, sizeof(AppConfig));
if (config == NULL) return NULL;
// 读取文件内容
char *file_content = read_file(filename);
if (file_content == NULL) {
free(config);
return NULL;
}
// 解析JSON
cJSON *root = cJSON_Parse(file_content);
free(file_content); // 解析后文件内容不再需要
if (root == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL) {
fprintf(stderr, "Config parse error: %s\n", error_ptr);
}
free(config);
return NULL;
}
// 解析应用名称
const cJSON *app_name = cJSON_GetObjectItemCaseSensitive(root, "app_name");
if (cJSON_IsString(app_name) && app_name->valuestring != NULL) {
config->app_name = strdup(app_name->valuestring);
}
// 解析版本号
const cJSON *version = cJSON_GetObjectItemCaseSensitive(root, "version");
if (cJSON_IsArray(version) && cJSON_GetArraySize(version) == 3) {
const cJSON *major = cJSON_GetArrayItem(version, 0);
const cJSON *minor = cJSON_GetArrayItem(version, 1);
const cJSON *patch = cJSON_GetArrayItem(version, 2);
if (cJSON_IsNumber(major) && cJSON_IsNumber(minor) && cJSON_IsNumber(patch)) {
config->version[0] = (int)major->valuedouble;
config->version[1] = (int)minor->valuedouble;
config->version[2] = (int)patch->valuedouble;
}
}
// 解析端口号
const cJSON *port = cJSON_GetObjectItemCaseSensitive(root, "port");
if (cJSON_IsNumber(port)) {
config->port = (int)port->valuedouble;
}
// 解析允许的IP列表
const cJSON *allowed_ips = cJSON_GetObjectItemCaseSensitive(root, "allowed_ips");
if (cJSON_IsArray(allowed_ips)) {
config->allowed_ips_count = cJSON_GetArraySize(allowed_ips);
config->allowed_ips = (char**)malloc(sizeof(char*) * config->allowed_ips_count);
if (config->allowed_ips != NULL) {
cJSON *ip = NULL;
int i = 0;
cJSON_ArrayForEach(ip, allowed_ips) {
if (cJSON_IsString(ip) && ip->valuestring != NULL) {
config->allowed_ips[i] = strdup(ip->valuestring);
} else {
config->allowed_ips[i] = NULL;
}
i++;
}
}
}
// 解析数据库配置
const cJSON *database = cJSON_GetObjectItemCaseSensitive(root, "database");
if (cJSON_IsObject(database)) {
const cJSON *host = cJSON_GetObjectItemCaseSensitive(database, "host");
const cJSON *timeout = cJSON_GetObjectItemCaseSensitive(database, "timeout");
const cJSON *retries = cJSON_GetObjectItemCaseSensitive(database, "retries");
if (cJSON_IsString(host) && host->valuestring != NULL) {
config->database.host = strdup(host->valuestring);
}
if (cJSON_IsNumber(timeout)) {
config->database.timeout = (int)timeout->valuedouble;
}
if (cJSON_IsNumber(retries)) {
config->database.retries = (int)retries->valuedouble;
}
}
// 清理
cJSON_Delete(root);
return config;
}
// 释放配置结构体
void free_config(AppConfig *config) {
if (config == NULL) return;
free(config->app_name);
if (config->allowed_ips != NULL) {
for (int i = 0; i < config->allowed_ips_count; i++) {
free(config->allowed_ips[i]);
}
free(config->allowed_ips);
}
free(config->database.host);
free(config);
}
// 使用配置
void use_config(const AppConfig *config) {
if (config == NULL) return;
printf("Application Name: %s\n", config->app_name ? config->app_name : "N/A");
printf("Version: %d.%d.%d\n",
config->version[0], config->version[1], config->version[2]);
printf("Port: %d\n", config->port);
printf("Allowed IPs: ");
for (int i = 0; i < config->allowed_ips_count; i++) {
if (config->allowed_ips[i] != NULL) {
printf("%s ", config->allowed_ips[i]);
}
}
printf("\n");
printf("Database: %s:%d (retries: %d)\n",
config->database.host ? config->database.host : "N/A",
config->database.timeout,
config->database.retries);
}
int main() {
AppConfig *config = parse_config("config.json");
if (config != NULL) {
use_config(config);
free_config(config);
}
return 0;
}
常见问题与解决方案
内存泄漏问题
问题:使用cJSON时最常见的问题是内存泄漏。
解决方案:
- 始终确保每个
cJSON_Create*函数创建的对象最终都被cJSON_Delete释放 - 创建对象树时,遵循"谁创建谁释放"的原则
- 使用工具如Valgrind检测内存泄漏
- 实现一个简单的内存跟踪机制:
// 简单的内存跟踪宏
#define CJSON_CREATE(type, ...) do { \
type = cJSON_Create##__VA_ARGS__; \
if (type == NULL) { \
fprintf(stderr, "Memory allocation failed at %s:%d\n", __FILE__, __LINE__); \
goto error; \
} \
} while(0)
// 使用示例
cJSON *object;
CJSON_CREATE(object, Object);
cJSON *string;
CJSON_CREATE(string, String, "example");
JSON解析错误处理
问题:JSON解析失败时难以定位错误位置。
解决方案:
// 增强的JSON解析错误处理
cJSON *safe_parse_json(const char *json_str, const char *source) {
cJSON *root = cJSON_Parse(json_str);
if (root == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL) {
// 计算错误位置
long error_pos = error_ptr - json_str;
// 打印错误上下文
fprintf(stderr, "JSON parse error in %s at position %ld:\n", source, error_pos);
// 打印错误位置前后的内容
int context_size = 30;
long start = (error_pos > context_size) ? (error_pos - context_size) : 0;
long end = error_pos + context_size;
if (end > strlen(json_str)) end = strlen(json_str);
fprintf(stderr, "...%.*s[ERROR]%.*s...\n",
(int)(error_pos - start), json_str + start,
(int)(end - error_pos), json_str + error_pos);
}
}
return root;
}
总结与最佳实践
核心要点总结
- 数组操作:掌握
cJSON_CreateArray、cJSON_AddItemToArray、cJSON_GetArrayItem和cJSON_ArrayForEach的使用 - 对象操作:熟练使用
cJSON_CreateObject、cJSON_AddItemToObject系列函数和cJSON_GetObjectItem - 内存管理:始终确保
cJSON_Delete与cJSON_Create*配对使用,释放根对象会递归释放所有子对象 - 错误处理:解析JSON时务必检查返回值,并使用
cJSON_GetErrorPtr获取错误信息 - 性能优化:对大型JSON使用预分配打印,对静态字符串使用StringReference
最佳实践清单
- 始终检查
cJSON_Create*函数的返回值,防止内存分配失败 - 解析JSON时使用
cJSON_Parse后立即检查返回值是否为NULL - 使用
cJSON_GetObjectItemCaseSensitive而非cJSON_GetObjectItem,避免键名大小写问题 - 释放JSON对象时只需调用根对象的
cJSON_Delete - 使用
cJSON_PrintPreallocated代替cJSON_Print提高性能 - 对于嵌入式系统,考虑增加内存分配钩子函数:
// 为嵌入式系统定制内存分配
void setup_cjson_memory_hooks(void) {
cJSON_Hooks hooks;
hooks.malloc_fn = my_custom_malloc; // 自定义malloc
hooks.free_fn = my_custom_free; // 自定义free
cJSON_InitHooks(&hooks);
}
进阶学习路线
- 源码阅读:深入阅读cJSON.c和cJSON.h源码,理解内部实现细节
- 单元测试:研究tests目录下的测试用例,学习各种边界情况处理
- 性能优化:针对特定场景优化JSON处理性能
- 扩展功能:学习cJSON_Utils提供的高级功能,如JSON Patch和指针操作
- 安全加固:了解JSON解析中的安全风险,如栈溢出和内存泄漏
通过掌握本文介绍的cJSON数组与对象操作技巧,你将能够高效处理各种JSON数据结构,为嵌入式系统和资源受限环境开发高性能的JSON处理模块。无论是配置文件解析、API数据交换还是日志记录,cJSON都能提供轻量级且可靠的JSON处理能力。
点赞、收藏、关注三连,获取更多嵌入式JSON处理技巧!下期预告:cJSON_Utils高级功能详解。
【免费下载链接】cJSON Ultralightweight JSON parser in ANSI C 项目地址: https://gitcode.com/gh_mirrors/cj/cJSON
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



