cJSON简介
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))
功能: