cJSON高级特性详解:数组与对象操作全攻略

cJSON高级特性详解:数组与对象操作全攻略

【免费下载链接】cJSON Ultralightweight JSON parser in ANSI C 【免费下载链接】cJSON 项目地址: 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_NULLNULL值
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_CreateArraycJSON_AddItemToArraycJSON_GetArrayItemcJSON_ArrayForEach的使用
  • 对象操作:熟练使用cJSON_CreateObjectcJSON_AddItemToObject系列函数和cJSON_GetObjectItem
  • 内存管理:始终确保cJSON_DeletecJSON_Create*配对使用,释放根对象会递归释放所有子对象
  • 错误处理:解析JSON时务必检查返回值,并使用cJSON_GetErrorPtr获取错误信息
  • 性能优化:对大型JSON使用预分配打印,对静态字符串使用StringReference

最佳实践清单

  1. 始终检查cJSON_Create*函数的返回值,防止内存分配失败
  2. 解析JSON时使用cJSON_Parse后立即检查返回值是否为NULL
  3. 使用cJSON_GetObjectItemCaseSensitive而非cJSON_GetObjectItem,避免键名大小写问题
  4. 释放JSON对象时只需调用根对象的cJSON_Delete
  5. 使用cJSON_PrintPreallocated代替cJSON_Print提高性能
  6. 对于嵌入式系统,考虑增加内存分配钩子函数:
// 为嵌入式系统定制内存分配
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);
}

进阶学习路线

  1. 源码阅读:深入阅读cJSON.c和cJSON.h源码,理解内部实现细节
  2. 单元测试:研究tests目录下的测试用例,学习各种边界情况处理
  3. 性能优化:针对特定场景优化JSON处理性能
  4. 扩展功能:学习cJSON_Utils提供的高级功能,如JSON Patch和指针操作
  5. 安全加固:了解JSON解析中的安全风险,如栈溢出和内存泄漏

通过掌握本文介绍的cJSON数组与对象操作技巧,你将能够高效处理各种JSON数据结构,为嵌入式系统和资源受限环境开发高性能的JSON处理模块。无论是配置文件解析、API数据交换还是日志记录,cJSON都能提供轻量级且可靠的JSON处理能力。

点赞、收藏、关注三连,获取更多嵌入式JSON处理技巧!下期预告:cJSON_Utils高级功能详解。

【免费下载链接】cJSON Ultralightweight JSON parser in ANSI C 【免费下载链接】cJSON 项目地址: https://gitcode.com/gh_mirrors/cj/cJSON

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值