JSON处理C函数库-cJSON
2024-10-28 14:26:43 阿炯

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

#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的版本号为例。


最新版本:1.7
v1.7.8于2024年5月发布。

项目主页:https://github.com/DaveGamble/cJSON