cJSON简介

发布时间 2023-11-15 17:27:20作者: 野哥李

CJSON简介

官网下载

cJSON download | SourceForge.net

github clone地址:https://github.com/yegeli/cJSON.git

gitee clone地址:https://gitee.com/yegeli/cJSON.git

核心内容

cJSON旨在成为一个可以应用在工作中的低门槛json解析器。该项目包含一个.c文件,和一个.h文件。

对JSON格式和结构的描述可以查看这个网站:http://www.json.org/
JSON是一种类似于XML的格式,不过更加轻量级。你能够使用它去移动数据,存储数据,或者仅仅用于描述项目的状态。

那么,如何使用cJSON呢?
添加cJSON.c到你的项目中,并且把cJSON.h放置到你的项目头文件的搜索路径中。
例如,你可以使用下面命令编译一个使用cJSON的小例子:(tests目录下有相关文件)

gcc cJSON.c test.c -o test -lm
./test

作为一个库,cJSON尽力完成它能够完成的各种琐碎工作。就像哲学具有两面性一样,你也能够通过两种方式使用cJSON:自动和手动。

接下来,让我们快速地走一遍cJSON的使用流程。

我从这个网站获取了一些JSON文件:http://www.json.org/fatfree.html
正是这个网站页面激发我写一个具有JSON哲学的cJSON解析器,简单,低门槛,别出心裁。
这是一份JSON数据:

{
    "name": "Jack (\"Bee\") Nimble", 
    "format": {
        "type":       "rect", 
        "width":      1920, 
        "height":     1080, 
        "interlace":  false, 
        "frame rate": 24
    }
}

假设你从一个文件,网站,或者一个神奇的精灵那里得到了这个JSON数据,你首先需要用一个char*指针指向它。然后你可以通过解析获得一个cJSON结构体。
解析JSON的方式:

cJSON *root = cJSON_Parse(my_json_string);

cJSON是一个对象。由于我们使用C语言实现,而C语言中没有对象的概念,所以我们使用结构体模拟对象。

接下来,获取framerate节点的值。

cJSON *format = cJSON_GetObjectItem(root,"format");
int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint;

改变framerate节点的值。

cJSON_GetObjectItem(format,"frame rate")->valueint=25;

对节点完成增删改查之后并不意味着完成了一切,每次完成操作后,都需要删除根节点,否则会出现内存泄露。

cJSON_Delete(root);

上述方式就是通过自动模式使用cJSON。当你使用自动模式时,你必须在你要解除对节点的引用前仔细检查指针是否释放。
接下来,将会演示如何使用代码生成一个完整的JSON。

cJSON *root,*fmt;
root=cJSON_CreateObject();	
cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
cJSON_AddStringToObject(fmt,"type",		"rect");
cJSON_AddNumberToObject(fmt,"width",		1920);
cJSON_AddNumberToObject(fmt,"height",		1080);
cJSON_AddFalseToObject (fmt,"interlace");
cJSON_AddNumberToObject(fmt,"frame rate",	24);

结构体讲解

typedef struct cJSON {
    /*     
    * next和prev用于遍历数组或对象的指针    
    * 使用如下API:GetArraySize/GetArrayItem/GetObjectItem    
    */   
    struct cJSON *next,*prev;      


    /* 数组或对象的孩子节点指针 */  
    struct cJSON *child;    


    /* 键的类型    
    * 取值分别是:False、Ture、NULL、Number、String、Array、Object    */   
    int type;

    /* 当type为String类型时,该字段可以取到值 */       
    char *valuestring;      


    /* 当type为Number是,期望value为int类型,则使用该字段取值 */   
    int valueint;      


    /* 当type为Number是,期望value为double类型,则使用该字段取值 */   
    double valuedouble;    


    /* 该字段存储key的字符长名字*/   
    char *string; 

} cJSON;

函数原型解析

/**  
  * 按照cJson结构体的结构解析json数据包,  
  * 该函数会分配一个malloc空间,需要手动释放  
  */ 
extern cJSON *cJSON_Parse(const char *value);  

/**  
  * 从序列化的cJson结构体中获取key的值  
  */ 
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);  


/**  
  * 释放序列化结构体时分配的内存空间  
  */ 
  extern void  cJSON_Delete(cJSON *c);

实战解析

解析键值对

//传入键值对数据包 
char *json_string = {"firstName":"Brett"};

cJSON *root=cJSON_Parse(json_string);  

cJSON *item = cJSON_GetObjectItem(root,"firstName");  

char *value = item->valuestring;  

cJSON_Delete(root);

解析结构体

//输入键值对数据包 
{   
    "person": {     
        "firstName": "z",     
        "lastName": "jadena",     
        "email": "jadena@126.com",     
        "age": 8,     
        "height": 1.17   
    } 
}  

//定义对应结构体 
typedef struct{   
    char firstName[32];     
    char lastName[32];     
    char email[64];     
    int age;     
    float height; 
} PERSON;  

cJSON *root = NULL; 
cJSON *object = NULL; 
cJSON *item = NULL; 
PERSON person;  
root=cJSON_Parse(json_string);  
object=cJSON_GetObjectItem(root,"person");  
item=cJSON_GetObjectItem(object,"firstName");  
memcpy(person.firstName,item->valuestring,strlen(item->valuestring));  
item=cJSON_GetObjectItem(object,"lastName");  
memcpy(person.lastName,item->valuestring,strlen(item->valuestring));  i
tem=cJSON_GetObjectItem(object,"email");  
memcpy(person.email,item->valuestring,strlen(item->valuestring));  
item=cJSON_GetObjectItem(object,"age"); 
person.age=item->valueint;  
item=cJSON_GetObjectItem(object,"height");  
person.height=item->valuedouble;  
cJSON_Delete(object); 

解析数组

//输入解析数据包 
{   
    "people": [     
        {       
            "firstName": "z",       
            "lastName": "Jason",       
            "email": "bbbb@126.com",       
            "height": 1.67     
        },     
        {       
            "lastName": "jadena",       
            "email": "jadena@126.com",       
            "age": 8,       
            "height": 1.17     
        },     
        {       
            "email": "cccc@126.com",       
            "firstName": "z",       
            "lastName": "Juliet",       
            "age": 36,       
            "height": 1.55     
        }   
    ] 
}  

cJSON *root, *list, *cell, *field;  
root = cJSON_Parse(cmdMsg);  
list = cJSON_GetObjectItem(root, "people");  
int size = cJSON_GetArraySize(list);  
for (int i = 0; i < size; i++) {   
    cell = cJSON_GetArrayItem(list, i);   
    field = cJSON_GetObjectItem(cell, "firstName");   
    field = cJSON_GetObjectItem(cell, "lastName");   
    field = cJSON_GetObjectItem(cell, "email");   
    field = cJSON_GetObjectItem(cell, "age"); 
} 
cJSON_Delete(root); 

解析嵌套字典

生成json

void write_file_bin_data(const char *file, void *data, size_t byte_length)
{
    std::ofstream out(file, std::ios::out | std::ios::binary);
    out.write((char *)data, byte_length);
    out.close();
}

cJSON *root,*mag_dict_json;
root=cJSON_CreateObject();	

/* dict_peak假设此字典已存在
{
	"119":	{
		"117":	-12.719198,
        "64":	4.124886
	}
} */
for (const auto& entry : dict_peak) {
    int key = entry.first;
    const unordered_map<int, float>& mag_dict = entry.second;
    cJSON_AddItemToObject(root, to_string(key).c_str(), mag_dict_json=cJSON_CreateObject());
    for (const auto& value_entry : mag_dict) {
        int value = value_entry.first;
        float db_mic = value_entry.second;
        cJSON_AddNumberToObject(mag_dict_json, to_string(value).c_str(),db_mic);
    }
}

char *out;
out=cJSON_Print(root);
cJSON_Delete(root);	
write_file_bin_data("./res.json", out, strlen(out));
free(out);	

json文件

{
	"119":	{
		"117":	-12.719198
	},
	"32":	{
		"96":	-10.734543,
		"64":	4.124886
	}
}

读取json到dict_peak字典

//从文件读json数据到string
void Read_cJSON(const char *path, string &data) {
	ifstream fr(path);
	if (!fr) {
		cout << "can`t open file!" << endl;
		return;
	}
	char ch;
	while ((ch = fr.get()) != EOF) {
		data.push_back(ch);
	}
	fr.close();
}

unordered_map<int, unordered_map<int, float>> dict_peak;
string data;
Read_cJSON("./res.json", data);
cJSON *root = cJSON_Parse(data.c_str());
for (int j = 0; j < cJSON_GetArraySize(root); j++) {
    cJSON* out1 = cJSON_GetArrayItem(root, j);
    cJSON *root2 = cJSON_GetObjectItem(root,out1->string);
    unordered_map<int, float> innerMap;
    for (int k = 0; k < cJSON_GetArraySize(root2); k++) {
        cJSON* out2 = cJSON_GetArrayItem(root2, k);
        innerMap[stoi(out2->string)] = out2->valuedouble;
    }
    dict_peak[stoi(out1->string)] = innerMap;
}
cJSON_Delete(root);

cJSON内存管理

cJSON库提供了钩子函数来帮助用户自定义内存管理函数,如果不设置,这默认为malloc和free。

自动模式下的内存管理

在自动模式下,cJSON使用默认的malloc和free函数管理内存。
在cJSON中,每个节点都是malloc而来,每个节点的string和valuestring也是malloc而来。
使用cJSON库中,使用cJSON_Delete函数可以递归释放JSON树中malloc的节点内存和字符内存。
当使用cJSON_Print函数后,需要手动释放cJSON_Print函数分配的内存,避免内存泄露。

手动模式下的内存管理

在手动模式下,需要指定钩子cJSON_Hooks的指向。

cJSON_Hooks结构如下:

typedef struct cJSON_Hooks {
	void *(*malloc_fn)(size_t sz);
	void (*free_fn)(void *ptr);
} cJSON_Hooks;

只要通过cJSON_Hooks指定了内存分配和释放的函数,在之后的使用中将自动使用指定的函数进行内存分配和释放。
假设已经有了一个自定义的内存分配函数my_malloc, 内存释放函数my_free。使用如下:

cJSON_InitHooks *hooks = NULL;
hooks = (cJSON_InitHooks *)calloc(1, sizeof(cJSON_InitHooks));
hooks->malloc_fn = my_malloc;
hooks->free_fn = my_free;
cJSON_InitHooks(hooks);

使用上述代码后,程序就能够自动使用自定义的内存释放和分配函数。

cJSON函数列表


常用解析函数
宏定义


常用解析函数

cJSON *cJSON_Parse(const char *value)
参数:
    value(const char*):char*指针,指向待解析的JSON数据
返回值:
    JSON树的根节点
函数功能:
    解析JSON数据,将数据填入JSON树中
输入示例:
    {
        "name": "Jack (\"Bee\") Nimble", 
        "format": {"type":       "rect", 
        "width":      1920, 
        "height":     1080, 
        "interlace":  false,
        "frame rate": 24
    }
char  *cJSON_Print(cJSON *item)
参数:
    item(cJSON *):cJSON根节点
返回值:
    item节点解析后的全树字符串
函数功能:
    从item节点开始递归遍历,将节点树转换为字符串
输出示例:
    {
    	"name":	"Jack (\"Bee\") Nimble",
    	"format":	{
    		"type":	"rect",
    		"width":	1920,
    		"height":	1080,
    		"interlace":	false,
    		"frame rate":	24
    	}
    }
备注:
    使用该函数后,需要根据返回的char *指针释放内存。
    eg:
        out=cJSON_Print(json);
		printf("%s\n",out);
		free(out);
void   cJSON_Delete(cJSON *c)
参数:
    c(cJSON):cJSON根节点
返回值:
    无
函数功能:
    从根节点c开始递归删除JSON树各个节点,释放内存。
cJSON *cJSON_CreateObject(void)
参数:
    无
返回值:
    指向一个cJSON_Object类型节点的指针
函数功能:
    创建一个cJSON节点,并设置节点类型为cJSON_Object
cJSON *cJSON_CreateString(const char *string)
参数:
    string(const char *):新创建节点的名称
返回值:
    cJSON_String类型的节点的指针
函数功能:
    创建一个cJSON_Object类型的节点,并且将节点的值valuestring设置为string
cJSON *cJSON_CreateNumber(double num)	
参数:
    num(double):新创建节点的值
返回值:
    cJSON_String类型的节点的指针
函数功能:
    创建一个cJSON_Number类型的节点,并且将节点的值valuedouble设置为num, valueint设置为(int)num
cJSON *cJSON_CreateArray(void)	
参数:
    无
返回值:
    cJSON_Array类型的节点的指针
函数功能:
    创建一个cJSON_Array类型的节点
cJSON *cJSON_CreateBool(int b)		
参数:
    b(int):新创建节点的类型,非0为cJSON_True,0为cJSON_False
返回值:
    cJSON_False/cJSON_True类型的节点的指针
函数功能:
    创建一个cJSON_False/cJSON_True类型的节点
cJSON *cJSON_CreateTrue(void)	
参数:
    无
返回值:
    cJSON_True类型的节点的指针
函数功能:
    创建一个cJSON_True类型的节点
cJSON *cJSON_CreateFalse(void)	
参数:
    无
返回值:
    cJSON_False类型的节点的指针
函数功能:
    创建一个cJSON_False类型的节点
cJSON *cJSON_CreateNull(void)	
参数:
    无
返回值:
    cJSON_Null类型的节点的指针
函数功能:
    创建一个cJSON_Null类型的节点
void	cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item)
参数:
    object(cJSON *):被添加节点的节点
    string(char *):要添加节点的名称
    item(cJSON *):要添加节点
返回值:
    无
函数功能:
    将item节点的名称设置为string。如果object节点有没有子节点,就将item设置为object子节点,否则将item添加object->child链表的尾部,成为object->child的兄弟节点
void cJSON_AddItemToArray(cJSON *array, cJSON *item)
参数:
    
返回值:
    无
函数功能:
    
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
参数:
    
返回值:
    无
函数功能:
    
void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item)
参数:
    
返回值:
    无
函数功能:
    

宏定义

#define cJSON_AddNullToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateNull())
功能:
    创建一个string值为name的cJSON_Null节点,并添加到object。
#define cJSON_AddTrueToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
功能:
    创建一个string值为name的cJSON_True节点,并添加到object。
#define cJSON_AddFalseToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
功能:
    创建一个string值为name的cJSON_False节点,并添加到object。
#define cJSON_AddBoolToObject(object,name,b)	cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
功能:
    创建一个string值为name的cJSON_Bool节点,并添加到object。b非0为cJSON_True,0为cJSON_False。
#define cJSON_AddNumberToObject(object,name,n)	cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
功能:
    创建一个string值为name,valuedouble为n,valueint为(int)n的cJSON_Number节点,并添加到object。
#define cJSON_AddStringToObject(object,name,s)	cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
功能:
    创建一个string值为name,valuestring为s的cJSON_String节点,并添加到object。
#define cJSON_SetIntValue(object,val)			((object)?(object)->valueint=(object)->valuedouble=(val):(val))
功能:
    
#define cJSON_SetNumberValue(object,val)		((object)?(object)->valueint=(object)->valuedouble=(val):(val))
功能: