C环境下更简洁的cJSON辅助函数

发布于:2025-06-11 ⋅ 阅读:(30) ⋅ 点赞:(0)

2023年做过一个在C环境下,用来解析.json配置信息的辅助函数族,是用cJSON这个工具来做的一层封装: 

一组完整的读Json配置信息的辅助函数_json帮助函数-CSDN博客

这一次进行代码重构时,在AI的帮助下,意识到这样的事:

json的规范里,并未定义路径分隔符,这就是为啥json解析时用到的那个cJSON那个接口会不太好用的原因。然后就有了对他的一个扩展:

1.为cJSON补充路径数列访问能力

//将.json文件整体读入一个json Obj
cJSON *json_read_text_file(const char *filename){
    FILE *file = fopen(filename, "r");
    if (file == NULL) {
        printf("Failed to open the file.\n");
        return NULL;
    }

    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    fseek(file, 0, SEEK_SET);

    char *file_content = (char *)malloc(file_size + 1);
    fread(file_content, 1, file_size, file);
    file_content[file_size] = '\0';

    fclose(file);

    cJSON *json = cJSON_Parse(file_content);
    free(file_content);
    if (json == NULL) {
        printf("Failed to parse JSON.\n");
        free(file_content);
        return NULL;
    }


    return json;
}


//一个扩展:可以直接使用反斜杠路径提示符访问任何一个json中间节点和叶子节点
cJSON* gpJson_GetItemFromPath(cJSON* root, const char* path) {
    cJSON* current = root;
    char* path_copy = strdup(path);  // 复制路径
    char* token = strtok(path_copy, "\\");  // 使用 "\" 分割路径

    while (token != NULL) {
        if (current == NULL) break;  // 提前终止

        // 检查 token 是否包含数组下标(如 `key[1]`)
        char* bracket = strchr(token, '[');
        if (bracket != NULL && bracket[1] >= '0' && bracket[1] <= '9') {
            // 提取键名(`key` 部分)
            char* key = strndup(token, bracket - token);

            *bracket = '\0';  // 临时截断字符串

            // 提取下标(`1` 部分)
            int index = 0;
            char* end_bracket = strchr(bracket + 1, ']');
            if (end_bracket != NULL) {

                *end_bracket = '\0';  // 临时截断
                index = atoi(bracket + 1);  // 字符串转整数
            }

            // 获取对象中的键值
            current = cJSON_GetObjectItem(current, key);
            free(key);  // 释放临时键名内存

            // 若当前节点是数组且下标有效,则访问数组元素
            if (current != NULL && cJSON_IsArray(current) && index >= 0 && index < cJSON_GetArraySize(current)) {
                current = cJSON_GetArrayItem(current, index);
            } else {
                current = NULL;  // 无效访问
            }
        } else {
            // 普通键名访问
            current = cJSON_GetObjectItem(current, token);
        }
        token = strtok(NULL, "\\");
    }

    free(path_copy);  // 释放复制的路径
    return current;
}

2.用法演示

//得到配置
    int i = 0;
    const char topic[1024];
    printf("sample group idx = %d\n", idxGroup);
    fflush(stdout);

//片段1: 得到jsonObj["channels"][3]["source"]["desc"]["ipAddr"], 一个string
    snprintf(topic, 1024, "channels[%d]\\source\\desc\\ipAddr", idxGroup);
    strncpy(papp->ip, cJSON_GetStringValue(gpJson_GetItemFromPath(jsonCfg, topic)), sizeof(papp->ip));



//片段2: 得到jsonObj["channels"][3]["source"]["desc"]["saps"], 一个integer
    snprintf(topic, 1024, "channels[%d]\\source\\desc\\saps", idxGroup);
    papp->saps = (int)cJSON_GetNumberValue(gpJson_GetItemFromPath(jsonCfg, topic));

    ....

//片段3:路径上可以包含任意的array:jsonObj["channels"][3]["source"]["desc"]["oem_ch"][5]
//    
    snprintf(topic, 1024, "channels[%d]\\source\\desc\\oem_ch[%i]", idxGroup, i);
    cJSON* dumb = gpJson_GetItemFromPath(jsonCfg, topic);
    if(dumb == NULL){
        papp->ch_src_list[i] = -1;
        needCheck = 0;
        continue;
    }
    else{
        papp->ch_src_list[i] = (int)cJSON_GetNumberValue(dumb);
        printf("% 2d", papp->ch_src_list[i]);
    }

3.结论及扩展

3.1 现在辅助函数只有两个函数。

        1.读取.json文件;
        2.然后就是解析全路径key反斜杠字符串。

3.2 如果使用C++语法,其实完全可以构造出一个支持: 
       jsonObj["channels"][3]["source"]["desc"]["saps"],这种语法规则的json读写类。

3.3 所有的终端叶子节点的json格式到char *, float之类的数据对象的转换,可以完全用cJSON本身的IO函数处理。不必画蛇添足,做出一堆比如
gpJson_get_string_item()
gpJson_get_float_item()一类的东西。

3.4 cJSON似乎没有处理default值,感觉这个地方可用宏来解决,更简单。仿照示例3.