1.前言

​最近本人在学习使用cJSON库中的API使用,我根据自身的学习情况,梳理了各个常用API的主要功能,提供给大家学习和参考,有错误的地方欢迎大家指出。

2.库简介

cJSON是一个轻量级的,基于C语言实现的,可实现JSON文件解析和生成的开源库。git仓库为:
https://github.com/DaveGamble/cJSON。使用该库,仅需要把cJSON.c和cJSON.h两个文件放入到项目中,即可使用。

图一
本教程的文件目录主要如下:

  • cJSON.c
  • cJSON.h
  • main.c
  • Makefile

目录结构较为简单。其中Makefile文件可自行编写。在main.c中编写如下代码。之后运行make指令,执行main.exe后,若能输出cJSON版本信息,则代表cJSON库移植成功。

main.c代码

#include <stdio.h>
#include "cJSON.h"

int main()
{
    const char *version = cJSON_Version();
    printf("cJSON version: %s\n", version);
    return 0;
}

执行结果

PS C:\Users\17464\Desktop\cjson> .\main.exe
cJSON version: 1.7.19
PS C:\Users\17464\Desktop\cjson> 

3. JSON简介

JSON是一种轻量级的数据交换格式,

3.1. 数据类型

数据类型 示例
对象 (Object) 花括号 { }表示一个对象
数组 (Array) 方括号 [ ]表示,如 [1, 2, 3]
字符串 (String) 用双引号表示,如 “hello”
数值(Number) 1 或 -1 或 3.14
布尔值 (Boolean) true 或 false,必须小写
空值 (Null) null,必须小写

json主要支持以上6种数据类型,基本上满足了各种数据类型的表示。在实际开发中,可根据实际数据类型进行灵活的组合,下面提供一个参考的json数据。

3.2. JSON格式

{
    "name"   : "mark",
    "age"    : 22,
    "work"   : true,
    "date"   :[2025,10,25],
    "girlfriend" : null,

    "friends": [
        {
            "name"    : "John",
            "age"     : 20,
            "work"    : false,
            "address" : ["China","Zhejiang","Hangzhou"]
        },
        {
            "name"    : "Jane",
            "age"     : 23,
            "work"    : true
        }
    ]
}

通过观察上述示例的数据结构,可以得出以下结论:

  1. 数组内的元素可以是数字,也可以是字符串。
  2. 数组内可以嵌套对象。
  3. 对象内的元素必须满足键值对的格式,即key : value。
  4. 在对象和数组中,除最后一个数据,都要用逗号“ ,” 隔开。

4. API函数介绍

经过简单的铺垫,下面直接进入主题,开始介绍下cJSON库中各种常用API,我将API主要分成解析、打印、查找、类型判断、创建、添加、修改这几个板块。

4.1. 解析

4.1.1. cJSON_Parse

/**
 * @brief 解析JSON字符串,生成一个链表,为后续API提供入参
 * 
 * @param value JSON字符串
 * @return cJSON* 返回cJSON链表
 */
(cJSON *) cJSON_Parse(const char *value);

4.2. 打印

4.2.1. cJSON_Print

/**
 * @brief 打印cJSON链表为JSON字符串
 * 
 * @param item cJSON链表
 * @return char* JSON字符串
 */
(char *) cJSON_Print(const cJSON *item);

4.2.2. cJSON_PrintUnformatted

/**
 * @brief 打印cJSON链表为JSON字符串(压缩)
 * 
 * @param item cJSON链表
 * @return char* 压缩处理的JSON字符串
 */
(char *) cJSON_PrintUnformatted(const cJSON *item);

4.3. 数据查找

4.3.1. cJSON_GetObjectItem

/**
 * @brief 查找指定键,不区分大小写
 * 
 * @param object cJSON链表
 * @param string 待查找键名
 * @return cJSON* 存储该数据信息的结构体
 */
(cJSON *) cJSON_GetObjectItem(const cJSON * const object,
							  const char * const string);

4.3.2. cJSON_GetObjectItemCaseSensitive

/**
 * @brief 查找指定键,区分大小写
 * 
 * @param object cJSON链表 
 * @param string 待查找键名
 * @return cJSON* 存储该数据信息的结构体
 */
(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object,
										   const char * const string);

4.4. 数据类型判断

以下接口可以用来判断cJSON * 类型数据属于JSON哪种的数据类型,其接口如下:

API 功能
cJSON_IsObject 检查数据是否为对象
cJSON_IsArray 检查数据是否为数组
cJSON_IsString 检查数据是否为字符串
cJSON_IsNumber 检查数据是否为数值
cJSON_IsBool 检查数据是否为布尔值
cJSON_IsNull 检查数据是否为空值

这些API的入参都是cJSON *类型,可以用来判断查找API来确定返回的CJSON *为哪种数据类型。

4.5. 数据读取

4.5.1. cJSON_GetStringValue

/**
 * @brief 查找指定键的对应字符串值
 * 
 * @param itemc 输入键
 * @return char* 对应键的字符串值
 */
(char *) cJSON_GetStringValue(const cJSON * const item)

4.5.2. cJSON_GetNumberValue

/**
 * @brief 查找指定键的对应数值
 * 
 * @param itemc 输入键
 * @return char* 对应键的数值
 */
(double) cJSON_GetNumberValue(const cJSON * const item)

4.6. 创建数据

API 功能
cJSON_CreateObject 创建对象类型数据
cJSON_CreateArray 创建数组类型数据
cJSON_CreateString 创建字符串类型数据
cJSON_CreateNumber 创建数值类型数据
cJSON_CreateBool 创建布尔值类型数据
cJSON_CreateNull 创建空值类型数据

4.7. 添加数据

以下是快捷添加的API

API 功能
cJSON_AddObjectToObject 在对象中添加对象类型
cJSON_AddArrayToObject 在对象中添加数组类型
cJSON_AddStringToObject 在对象中添加字符串类型
cJSON_AddNumberToObject 在对象中添加数值类型
cJSON_AddBoolToObject 在对象中添加布尔值类型
cJSON_AddNullToObject 在对象中添加空值类型

还有一些API是手动添加的,主要是搭配创建数据API,完成数据的创建后,在执行下面的API添加到对象或者数组中。

  • cJSON_AddItemToObject()
  • cJSON_AddItemToArray()

5. 注意事项

  1. cJSON库中通过API返回的char *和cJSON *类型数据都是通过动态内存申请得到,在不需要使用这些数据时,需要释放内存,防止内存碎片化。其中char *类型的数据通过cJSON_free释放。cJSON *类型的数据通过cJSON_Delete释放,并且由于cJSON支持递归删除,只需要删除根节点即可。

  2. 同时cJSON内存管理的接口,默认使用c标准库的函数,及malloc、free、realloc。若在操作系统中使用cJSON库,建议更换成操作系统提供的内存管理接口。如使用FreeRTOS实时操作系统,需将
    internal_malloc这个宏对应的malloc替换成pvPortMalloc
    internal_free这个宏对应的free替换成vPortFree

6. 应用示例

6.1. JSON读取

以下代码为读取test.json内的信息,并打印json表内容,以及特定键值对。

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"

/**
 * @brief 从文件读取 JSON 字符串
 * 
 * @param filename 文件路径
 * @return char* JSON 字符串(需要调用者 free),失败返回 NULL
 */
char* read_json_file(const char *filename) {
    FILE *file = NULL;
    char *buffer = NULL;
    long file_size = 0;
    size_t read_size = 0;
    
    // 打开文件
    file = fopen(filename, "r");
    if (file == NULL) {
        fprintf(stderr, "Error: Cannot open file '%s'\n", filename);
        return NULL;
    }
    
    // 获取文件大小
    fseek(file, 0, SEEK_END);
    file_size = ftell(file);
    fseek(file, 0, SEEK_SET);
    
    if (file_size < 0) {
        fprintf(stderr, "Error: Cannot determine file size\n");
        fclose(file);
        return NULL;
    }
    
    // 分配内存(+1 用于 '\0')
    buffer = (char*)malloc(file_size + 1);
    if (buffer == NULL) {
        fprintf(stderr, "Error: Memory allocation failed\n");
        fclose(file);
        return NULL;
    }
    
    // 读取文件内容
    read_size = fread(buffer, 1, file_size, file);
    buffer[read_size] = '\0';  // 添加字符串结束符
    
    fclose(file);
    
    return buffer;
}

int main()
{
    /* 读取JSON文件 */
    char* json_string   = read_json_file("test.json");

    /* 解析JSON字符串 */
    cJSON *cjson_parse  = cJSON_Parse(json_string);

    /* 打印JSON字符串 */
    char *print_json = cJSON_Print(cjson_parse);
    printf("print_json: \n%s\n", print_json);
    cJSON_free(print_json);


    cJSON *get_name = cJSON_GetObjectItem(cjson_parse,"name");
    if(cJSON_IsString(get_name))
    {
        printf("get_name: %s\n", cJSON_GetStringValue(get_name));
    }

    cJSON *get_age = cJSON_GetObjectItem(cjson_parse,"age");
    if(cJSON_IsNumber(get_age))
    {
        printf("get_age: %f\n", cJSON_GetNumberValue(get_age));
    }

    cJSON_Delete(cjson_parse);
    
    return 0;
}

输出

print_json: 
{
        "name": "mark",
        "age":  22,
        "work": true,
        "date": [2025, 10, 25],
        "girlfriend":   null,
        "friends":      [{
                        "name": "John",
                        "age":  20,
                        "work": false,
                        "address":      ["China", "Zhejiang", "Hangzhou"]
                }, {
                        "name": "Jane",
                        "age":  23,
                        "work": true
                }]
}
get_name: mark
get_age: 22.000000

6.2. JSON生成

以下代码为生成test.json内的信息,并打印生成后的json表内容。

main.c

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "cJSON.h"

int main()
{
    /* 创建一个JSON对象 */
    cJSON *out_json = cJSON_CreateObject();

    /* 添加字符串类型数据 */
    cJSON_AddStringToObject(out_json,   "name", "mark");

    /* 添加数值类型数据 */
    cJSON_AddNumberToObject(out_json,   "age",  22);

    /* 添加布尔类型数据 */
    cJSON_AddBoolToObject(out_json,     "work", true);

    /* 添加数组类型数据 */
    cJSON *date_array = cJSON_CreateArray();
    /* 添加数组元素 */
    cJSON_AddItemToArray(date_array, cJSON_CreateNumber(2025));
    cJSON_AddItemToArray(date_array, cJSON_CreateNumber(10));
    cJSON_AddItemToArray(date_array, cJSON_CreateNumber(25));
    cJSON_AddItemToObject(out_json, "date", date_array);

    /* 添加数组类型数据 */
    cJSON *friends_array = cJSON_CreateArray();
    /* 添加数组元素 */

    /* 创建friend1对象 */
    cJSON *friend1 = cJSON_CreateObject();
    cJSON_AddStringToObject(friend1, "name", "John");
    cJSON_AddNumberToObject(friend1, "age", 20);
    cJSON_AddBoolToObject(friend1, "work", false);

    /* 在数组元素中添加friend1对象 */
    cJSON_AddItemToArray(friends_array, friend1);

    /* 创建friend2对象 */
    cJSON *friend2 = cJSON_CreateObject();
    cJSON_AddStringToObject(friend2, "name", "Jane");
    cJSON_AddNumberToObject(friend2, "age", 23);
    cJSON_AddBoolToObject(friend2, "work", true);

    /* 在数组元素中添加friend2对象 */
    cJSON_AddItemToArray(friends_array, friend2);

    /* 在JSON对象中添加friends数组 */
    cJSON_AddItemToObject(out_json, "friends", friends_array);

    /* 打印JSON对象 */
    char *print1_json = cJSON_Print(out_json);
    printf("print1_json: \n%s\n", print1_json);
    cJSON_free(print1_json);

    /* 压缩打印JSON对象 */
    char *print2_json = cJSON_PrintUnformatted(out_json);
    printf("print2_json: \n%s\n", print2_json);
    cJSON_free(print2_json);

    /* 释放内存 */
    cJSON_Delete(out_json);

    return 0;
}

输出

print1_json: 
{
        "name": "mark",
        "age":  22,
        "work": true,
        "date": [2025, 10, 25],
        "friends":      [{
                        "name": "John",
                        "age":  20,
                        "work": false
                }, {
                        "name": "Jane",
                        "age":  23,
                        "work": true
                }]
}
print2_json: 
{"name":"mark","age":22,"work":true,"date":[2025,10,25],"friends":[{"name":"John","age":20,"work":false},{"name":"Jane","age":23,"work":true}]}
PS C:\Users\17464\Desktop\cjson>

7. 总结

cJSON 是一个优秀的基于C语言的开源库,其简洁的API设计具有很高的学习价值。通过本文的学习,相信读者已经掌握了cJSON的基本使用方法。后续笔者将编写更多有价值的内容,希望得到大家的支持。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐