实现功能:解析JSON
- 更多模块移植与使用:https://gitee.com/thin-wind/Module
- 源码来源:https://sourceforge.net/projects/cjson/
- 注意事项
- 使用前先使用cJSON_InitHooks函数绑定当前平台malloc个free函数(嵌入式平台这里需要注意,能够直接使用malloc及free函数的平台可以不调用cJSON_InitHooks函数初始化cJSON)
- 当读取json文本文档时,注意文档编码格式改为“UTF-8”格式,还有注意中文乱码问题
- cJSON源码很多情况下只申请内存,使用完成后并不释放内存,所以需自己手动释放内存,以免造成内存泄漏
- 别删除“LICENSE”文件
- 移植及使用说明:
- 下载cJSON工程源码后,其中有效文件仅一个cJSON.c和cJSON.h文件,另外移植时“LICENSE”文件保留,其它均可删除。
- 参考网上各种资料,都是直接一开始对cJSON结构体做讲解,云里雾里讲了一堆,我个人认为先研究数据结构并不科学,应该先从方法(即函数)开始研究,函数涉及到哪个变量,再仔细分析这个变量。所以这里只需要先简单了解一下cJSON结构体就行。
/* cJSON结构体 */ typedef struct cJSON { struct cJSON *next,*prev; /* 遍历数组或对象链的前向或后向链表指针*/ struct cJSON *child; /* 数组或对象的孩子节点*/ int type; /* key的类型*/ char *valuestring; /* 字符串值*/ int valueint; /* 整数值*/ double valuedouble; /* 浮点数值*/ char *string; /* key的名字*/ } cJSON;
- 先来了解5个常用的函数
/* ** 作用:将字符串value按照cJSON结构体的结构序列化,并在堆中开辟一块内存存储cJSON结构体 ** 返回值:成功返回一个指向内存块中的cJSON的指针,失败返回NULL */ cJSON *cJSON_Parse(const char *value); /* ** 作用:获取数组成员或对象个数 ** 返回值:数组成员或者对象个数 */ int cJSON_GetArraySize(cJSON *array); /* ** 作用:获取JSON字符串字段值 ** 返回值:成功返回一个指向cJSON类型的结构体指针,失败返回NULL */ cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); /* ** 作用:将cJSON数据解析成JSON字符串,并在堆中开辟一块char*的内存空间存储JSON字符串 ** 返回值:成功返回一个char*指针该指针指向位于堆中JSON字符串,失败返回NULL */ char *cJSON_Print(cJSON *item); /* ** 作用:释放位于堆中cJSON结构体内存 ** 返回值:无 */ void cJSON_Delete(cJSON *c); • 假设json_string字符串内容如下【json的基本单元可以理解成一组键值对(key:value)】: { "key1":"str_value", "key2":12, "key3":13.0 } • 示例代码: #include <stdio.h> #include "cJSON.h" int main(void) { cJSON *cjson; cJSON *cjson_item; /* 我们一般从文件中读取字符串到json_string,为了测试方便直接给json_string赋值 */ char json_string[] = "{ \ \"key1\":\"str_value\", \ \"key2\":12, \ \"key3\":13.0 \ }"; /* 我们首先将json_string这个字符串序列化成cJSON格式,并给cJSON结构申请内存 */ cjson = cJSON_Parse(json_string); /* 判断cJSON_Parse函数返回值确定是否序列化成功 */ if(cjson == NULL) { printf("cJSON_Parse err\n"); return -1; } printf("cJSON_Parse ok\n"); /* 打印cjson对象成员个数 */ printf("num = %d\n", cJSON_GetArraySize(cjson)); /* 依次打印key对应的value值 */ printf("key1 = %s\n", cJSON_GetObjectItem(cjson,"key1")->valuestring); printf("key2 = %d\n", cJSON_GetObjectItem(cjson,"key2")->valueint); printf("key3 = %lf\n", cJSON_GetObjectItem(cjson,"key3")->valuedouble); printf("-----------------------\n"); /* 另一种方式 */ /* 获取cjson第一个成员对象 */ cjson_item = cjson->child; /* 打印第一个成员key和value */ printf("%s = %s\n", cjson_item->string, cjson_item->valuestring); /* 指向cjson下一个成员对象 */ cjson_item = cjson_item->next; /* 打印下一个成员key和value */ printf("%s = %d\n", cjson_item->string, cjson_item->valueint); /* 指向cjson下一个成员对象 */ cjson_item = cjson_item->next; /* 打印下一个成员key和value */ printf("%s = %lf\n", cjson_item->string, cjson_item->valuedouble); /* 别忘记释放内存 */ cJSON_Delete(cjson); return 0; }
- 上述代码运行结果:
cJSON_Parse ok num = 3 key1 = str_value key2 = 12 key3 = 13.000000 ----------------------- key1 = str_value key2 = 12 key3 = 13.000000 • 到这里是不是很easy?下面看一下如何使用cJSON解析数组! • 假设json_arr_string字符串内容如下: { "test_arr":[ { "key1":"str_value1", "key2":12, "key3":13.0 }, { "key1":"str_value2", "key2":22, "key3":23.0 }] }
- 示例代码:
#include <stdio.h> #include "cJSON.h" int main(void) { cJSON *cjson; cJSON *arr; cJSON *arr_child_item; int arr_size; int index; /* 我们一般从文件中读取字符串到json_arr_string,为了测试方便直接给json_arr_string赋值 */ char json_arr_string[] = "{ \ \"test_arr\":[ \ { \ \"key1\":\"str_value1\", \ \"key2\":12, \ \"key3\":13.0 \ }, \ { \ \"key1\":\"str_value2\", \ \"key2\":22, \ \"key3\":23.0 \ }] \ }"; /* 我们首先将json_arr_string这个字符串序列化成cJSON格式,并给cJSON结构申请内存 */ cjson = cJSON_Parse(json_arr_string); /* 判断cJSON_Parse函数返回值确定是否序列化成功 */ if(cjson == NULL) { printf("cJSON_Parse err\n"); return -1; } printf("cJSON_Parse ok\n"); /* 从cjson中获取test_arr数组对象 */ arr = cJSON_GetObjectItem(cjson,"test_arr"); /* 获取arr数组元素个数,用于数组遍历 */ arr_size = cJSON_GetArraySize(arr); // 2个 /* 获取arr数组对象的子对象 */ arr_child_item = arr->child; for(index = 0; index < arr_size; index++) { /* 依次打印arr_child_item的key对应的value值 */ printf("key1 = %s\n", cJSON_GetObjectItem(arr_child_item,"key1")->valuestring); printf("key2 = %d\n", cJSON_GetObjectItem(arr_child_item,"key2")->valueint); printf("key3 = %lf\n", cJSON_GetObjectItem(arr_child_item,"key3")->valuedouble); printf("---------------------------------\n"); /* 指向下一个子对象 */ arr_child_item = arr_child_item->next; } /* 别忘记释放内存 */ cJSON_Delete(cjson); return 0; }
- 上述代码运行结果:
cJSON_Parse ok key1 = str_value1 key2 = 12 key3 = 13.000000 --------------------------------- key1 = str_value2 key2 = 22 key3 = 23.000000 ---------------------------------
- 到此我们已经初步掌握了如何解析JSON,接下来我们看一下c如何创建JSON字符串
- 我们需要新学习4个函数
/* ** 作用:创建一个cJSON对象并为该cJSON对象申请内存空间 ** 返回值:成功返回该cJSON对象内存空间首地址,失败返回NULL */ cJSON *cJSON_CreateObject(void); /* ** 作用:创建一个字符串型数据项,还有cJSON_CreateNumber等函数与之功能类似,这里就不一一介绍了 ** 返回值:成功返回cJSON数据项指针,失败返回NULL */ cJSON *cJSON_CreateString(const char *string); /* ** 作用:将上一个函数创建的cJSON数据项与与其键“string”组成键值对一起添加到第一个函数创建的cJSON对象中 ** 返回值:无 */ void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); /* ** 作用:申请一块内存,cJSON对象转成字符串形式打印到该内存中,并返回该内存首地址 ** 返回值:成功返回该字符串所在内存的首地址,失败返回NULL */ char *cJSON_Print(cJSON *item);
- 示例代码【先看下面运行结果再看示例代码更容易】:
#include <stdio.h> #include "cJSON.h" #include <stdlib.h> int main(void) { cJSON *cjson; cJSON *cjson_item; char *print_out; /* 首先创建一个cJSON对象 */ cjson = cJSON_CreateObject(); /* 创建一个字符串型数据项,并与键“key1”组成一组键值对,添加到cjson对象中 */ cjson_item = cJSON_CreateString("str_value"); cJSON_AddItemToObject(cjson, "key1", cjson_item); /* ** 创建一个整型数据项,并与键“key2”组成一组键值对,添加到cjson对象中, ** 注:cJSON工程对整型和浮点型并没有很好的区分开来,内部实际上统一按浮点型处理 */ cjson_item = cJSON_CreateNumber(12); cJSON_AddItemToObject(cjson, "key2", cjson_item); /* 创建一个浮点型数据项,并与键“key3”组成一组键值对,添加到cjson对象中 */ cjson_item = cJSON_CreateNumber(13.1); cJSON_AddItemToObject(cjson, "key3", cjson_item); /* 将cJSON对象的内容解析为字符串并打印到到print_out中 */ print_out = cJSON_Print(cjson); /* 打印print_out字符串 */ printf("%s\n", print_out); /* 别忘记释放内存 */ free(print_out); cJSON_Delete(cjson); return 0; }
- 运行结果:
{ "key1": "str_value", "key2": 12, "key3": 13.100000 } • 上述看完了之后,下面我们来看一下如何将一个结构体转化成JSON字符串,结构体如下: typedef struct { char * key1; int key2; double key3; }STRUCT_TYPE;
- 示例代码:
#include <stdio.h> #include "cJSON.h" #include <stdlib.h> typedef struct { char * key1; int key2; double key3; }STRUCT_TYPE; int main(void) { cJSON *cjson_root, *cjson_struct, *cjson_item; char *print_out; /* 定义结构体struct_test并给其元素赋值 */ STRUCT_TYPE struct_test; struct_test.key1 = "str_value"; struct_test.key2 = 12; struct_test.key3 = 13.1; /* 首先创建一个cJSON跟对象,想象一下树状分支能更好的理解各对象之间的关系 */ cjson_root = cJSON_CreateObject(); /* 再次创建一个cJSON对象,用于挂载结构体成员的对象,挂载好结构体的成员后再挂载到跟对象上 */ cjson_struct = cJSON_CreateObject(); /* 创建一个字符串型数据项,并挂载到cjson_struct对象中 */ cjson_item = cJSON_CreateString(struct_test.key1); cJSON_AddItemToObject(cjson_struct, "key1", cjson_item); /* 创建一个整型数据项,并添加到cjson_root对象中 */ cjson_item = cJSON_CreateNumber(struct_test.key2); cJSON_AddItemToObject(cjson_struct, "key2", cjson_item); /* 创建一个cJSON浮点型数据项,并添加到cjson_root对象中 */ cjson_item = cJSON_CreateNumber(struct_test.key3); cJSON_AddItemToObject(cjson_struct, "key3", cjson_item); /* 将cjson_struct对象挂载到跟对象cjson_root上 */ cJSON_AddItemToObject(cjson_root, "struct_test", cjson_struct); /* 将cJSON对象的内容解析为字符串到print_out中 */ print_out = cJSON_Print(cjson_root); /* 打印print_out字符串 */ printf("%s\n", print_out); /* 别忘记释放内存 */ free(print_out); /* 只需要释放树跟对象就可以释放从这个树跟上所挂载的所有分支分配的内存 */ cJSON_Delete(cjson_root); return 0; } • 上述代码运行结果: { "struct_test": { "key1": "str_value", "key2": 12, "key3": 13.100000 } }
- 稍微增加点难度,将一个结构体数组转化成JSON字符串
- 我们又要认识2个新函数了
/* ** 作用:创建一个cJSON数组对象并为该cJSON对象申请内存空间 ** 返回值:成功返回该cJSON对象内存空间首地址,失败返回NULL */ cJSON *cJSON_CreateArray(void); /* ** 作用:将对象item添加到array数组对象中 ** 返回值:无 */ void cJSON_AddItemToArray(cJSON *array, cJSON *item); • 示例代码: #include <stdio.h> #include "cJSON.h" #include <stdlib.h> typedef struct { char * key1; int key2; double key3; }STRUCT_TYPE; int main(void) { cJSON *cjson_root, *cjson_struct_arr, *cjson_struct_item, *cjson_item; char *print_out; int index = 0; /* 定义结构体数组struct_test_arr并给其成员赋值 */ STRUCT_TYPE struct_test_arr[2]; struct_test_arr[0].key1 = "str_value1"; struct_test_arr[0].key2 = 12; struct_test_arr[0].key3 = 13.1; struct_test_arr[1].key1 = "str_value2"; struct_test_arr[1].key2 = 22; struct_test_arr[1].key3 = 23.1; /* 首先创建一个cJSON跟对象,想象一下树状分支能更好的理解各对象之间的关系 */ cjson_root = cJSON_CreateObject(); /* 创建一个cJSON数组对象cjson_struct_arr, 填充完成后在挂载到跟对象上 */ cjson_struct_arr = cJSON_CreateArray(); for(index = 0; index < 2; index++) { /* 创建一个cJSON对象,用于挂载结构体成员的对象,挂载好结构体的成员后再挂载到cjson_struct_arr数组成员对象上 */ cjson_struct_item = cJSON_CreateObject(); /* 创建一个字符串型数据项,并挂载到cjson_struct_item对象中 */ cjson_item = cJSON_CreateString(struct_test_arr[index].key1); cJSON_AddItemToObject(cjson_struct_item, "key1", cjson_item); /* 创建一个整型数据项,并添加到cjson_struct_item对象中 */ cjson_item = cJSON_CreateNumber(struct_test_arr[index].key2); cJSON_AddItemToObject(cjson_struct_item, "key2", cjson_item); /* 创建一个cJSON浮点型数据项,并添加到cjson_struct_item对象中 */ cjson_item = cJSON_CreateNumber(struct_test_arr[index].key3); cJSON_AddItemToObject(cjson_struct_item, "key3", cjson_item); /* 将cjson_struct_item对象挂载到跟对象cjson_struct_item上 */ cJSON_AddItemToArray(cjson_struct_arr, cjson_struct_item); } /* 将cjson_struct_arr对象挂载到跟对象cjson_root上 */ cJSON_AddItemToObject(cjson_root, "struct_test_arr", cjson_struct_arr); /* 将cJSON对象的内容解析为字符串到print_out中 */ print_out = cJSON_Print(cjson_root); printf("%s\n", print_out); /* 别忘记释放内存 */ free(print_out); /* 只需要释放树跟对象内存就可以释放从这个树跟上所挂载的所有分支分配的内存 */ cJSON_Delete(cjson_root); return 0; } • 运行结果: { "struct_test_arr": [{ "key1": "str_value1", "key2": 12, "key3": 13.100000 }, { "key1": "str_value2", "key2": 22, "key3": 23.100000 }] }
- 到这里便基本掌握了cJSON的使用,更多使用方法建议多分析分析源码。