JSON(JavaScript Object Notation)[1]是一种轻量级的数据交换格式。它采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
引用:
[1]百度百科词条“JSON”: https://baike.baidu.com/item/JSON/2462549?fr=aladdin
1.JSON
我们知道,JSON主要构建于两种结构:一种是“名称/值”对的集合,我们一般称之为对象(object),比如,
1 2 3 4 5 |
{ "name": "leo", "sex": "male", "age": 18 } |
另一种是值的有序列表(An ordered list of values),我们一般称之为数组(array),比如,
1 |
["leo", "lee", "jason"] |
实际工作中我们用到的JSON串一般会同时包含上述两种结构,并且还可能会彼此嵌套。
2.cJSON
虽然JSON起源于JavaScript语言,但如今许多编程语言都提供了生成和解析JSON格式数据的接口。本文主要介绍cJSON库的一般使用方法。
(1)搭建cJSON
由于cJSON库主要是由一个c文件和一个头文件构成,因此你可以直接将这两个文件复制到任何你需要的地方,只是在编译的时候需要注意包含头文件的路径即可(使用-I选项指定头文件路径)。
此外,还有一种方法:将cJSON库下载到你的linux中,然后使用cmake及make将相应的头文件及库文件安装到诸如/usr/include/等目录下,命令如下:
1 2 3 4 5 6 |
[root@localhost cJSON]# git clone git://github.com/DaveGamble/cJSON.git [root@localhost cJSON]# mkdir build [root@localhost cJSON]# cd build [root@localhost build]# cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off -DCMAKE_INSTALL_PREFIX=/usr [root@localhost build]# make [root@localhost build]# make DESTDIR=$pkgdir install |
至此,在项目中包含头文件:#include <cjson/cJSON.h>后就可使用cJSON库了。
(2)cJSON数据结构
cJSON数据结构如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* The cJSON structure: */ typedef struct cJSON { /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ struct cJSON *next; struct cJSON *prev; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ struct cJSON *child; /* The type of the item, as above. */ int type; /* The item's string, if type==cJSON_String and type == cJSON_Raw */ char *valuestring; /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ int valueint; /* The item's number, if type==cJSON_Number */ double valuedouble; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ char *string; } cJSON; |
其中,第四个字段:int type;表示此JSON条目的类型。但是,该类型字段是以位标志(bit-flag)的形式存储的,这就意味着仅通过比较type的值是无法判断该条目具体是什么类型的。
所以,针对如下条目类型,cJSON提供了形如cJSON_Is…的函数来判断条目的具体类型:
1 2 3 4 5 6 7 8 9 10 |
cJSON_Invalid:表示不包含任何值的无效项。可以使用cJSON_IsInvalid()函数来判断。 cJSON_False: 表示一个错误的布尔值。可以使用cJSON_IsFalse()或cJSON_IsBool()函数来判断。 cJSON_True: 表示一个正确的布尔值。可以使用cJSON_IsTrue()或cJSON_IsBool()函数来判断。 cJSON_NULL: 表示一个null值。可以使用cJSON_IsNull()函数来判断。 cJSON_Number: 表示一个数字值。可以使用cJSON_IsNumber()函数来判断。 cJSON_String: 表示一个字符串值。可以使用cJSON_IsString()函数来判断。 cJSON_Array: 表示一个数组值。可以使用cJSON_IsArray()函数来判断。 cJSON_Object: 表示一个对象值。可以使用cJSON_IsObject()函数来判断。 cJSON_Raw: 表示存储在valuestring中以0终止的字符数组的任何JSON。可以使用cJSON_IsRaw()函数来判断。 此外,还有cJSON_IsReference表示引用及cJSON_StringIsConst表示string指向的是一个常量字符串两种类型标志。 |
(3)使用cJSON
对每一种值类型来说,cJSON都提供了相应的形如cJSON_Create…()函数来创建它们。这些函数会分配一个cJSON结构体,相应地需要使用cJSON_Delete()函数来释放cJSON结构体所占用的空间。
对null, booleans, numbers以及strings这些基本类型,相应地有创建函数:
1 2 3 4 |
cJSON_CreateNULL(); cJSON_CreateTrue(), cJSON_CreateFalse(), cJSON_CreateBool(); cJSON_CreateNumber(); cJSON_CreateString(); |
对数组(array)类型而言,
1 2 3 4 5 6 7 8 9 |
1)cJSON_CreateArray()函数用于创建一个空数组,或者使用cJSON_CreateArrayReference()函数创建一个数组引用。需要注意的是,数组引用中的实际数据不会被cJSON_Delete()函数释放。 2)cJSON_AddItemToArray()函数用于向数组中添加条目,或者使用cJSON_AddItemReferenceToArray()函数添加其他的条目(item)、数组(array)、字符串(string)的引用到当前数组中。 3)cJSON_InsertItemInArray()函数用于向数组中插入一个条目。该函数需要提供插入索引的位置,插入指定位置之后,该位置之后的所有条目需要依次向右移动。 4)cJSON_DetachItemFromArray()函数用于从给定的索引处取出一个条目。需要注意的是,记得将该函数取出的返回值赋予一个指针再使用,否则会出现内存泄露。 5)cJSON_DeleteItemFromArray()函数用于删除一个条目。该函数与cJSON_DetachItemFromArray()类似,不同的是该函数是通过调用cJSON_Delete()函数删除分离的条目。 6)cJSON_ReplaceItemInArray()函数用于替换一个给定索引位置的条目,类似的cJSON_ReplaceItemViaPointer()函数用于替换指针指向的条目。 7)cJSON_GetArraySize()函数用于获取数组大小。 8)cJSON_GetArrayItem()函数用于获取指定索引上的元素。 9)其他。 |
需要注意的是:因为cJSON中的数组的底层是通过链表实现的,所以通过索引来遍历数组的话效率会很低(O(n^2)),因此cJSON提供了宏cJSON_ArrayForEach可以实现时间复杂度为O(n)的遍历数组。
类似地,对于对象(objects)类型而言,
1 2 3 4 5 6 7 8 9 10 |
1)cJSON_CreateObject()函数用于创建一个空对象,cJSON_CreateObjectReference()函数用于创建一个对象引用。 2)cJSON_AddItemToObject()函数用于向对象中添加一个条目。cJSON_AddItemToObjectCS()函数用于向名称(name)为常量(constant)或引用(reference)的对象中添加一个条目,类似的添加函数还有cJSON_AddStringToObject(), cJSON_AddArrayToObject(), cJSON_AddNumberToObject()等。 3)cJSON_DetachItemFromObjectCaseSensitive()函数用于从对象中取出一个条目。 4)cJSON_DeleteItemFromObjectCaseSensitive()函数删除对象中的一个条目。 5)cJSON_ReplaceItemInObjectCaseSensitive()及cJSON_ReplaceItemViaPointer()函数可用于替换对象中某个条目。 6)cJSON_GetArraySize()函数用于获取对象的大小。需要注意的是,对象内部是以数组形式存储的。 7)cJSON_GetObjectItemCaseSensitive()函数用来访问对象中的条目。 8)cJSON_ArrayForEach宏可用于遍历对象。 9)cJSON_AddNullToObject()函数用于快速创建一个新的空条目到对象中。 10)其他。 |
(4)解析JSON
使用cJSON_Parse()函数可以解析以0结尾的JSON字符串。
(5)打印JSON
cJSON库提供了如下打印JSON至字符串的函数:
1 2 3 4 |
cJSON_Print(); cJSON_PrintUnformatted(); cJSON_PrintBuffered(); cJSON_PrintPreallocated(); |
3.示例
(1)组装JSON
使用上述介绍的cJSON API,组装如下所示的JSON串:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ "name": "MIUI 4K", "resolutions": [ { "width": 1024, "height": 768 }, { "width": 1280, "height": 1024 }, { "width": 1920, "height": 1080 } ] } |
createJSON.c代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
#include <stdio.h> #include <cjson/cJSON.h> char *create_JSON() { char *string = NULL; cJSON *resolutions = NULL; size_t index = 0; const unsigned int resolution_numbers[3][2] = { {1024, 768}, {1280, 1024}, {1920, 1080} }; /*创建JSON对象monitor*/ cJSON *monitor = cJSON_CreateObject(); /*在monitor对象中添加第一个子条目("name": "MIUI 4K"), 该条目为string类型*/ if(cJSON_AddStringToObject(monitor, "name", "MIUI 4K") == NULL){ fprintf(stderr, "cJSON_AddStringToObject failed\n"); cJSON_Delete(monitor); return NULL; } /*在monitor对象中添加第二个子条目(resolutions), 该条目为数组类型*/ resolutions = cJSON_AddArrayToObject(monitor, "resolutions"); if(resolutions == NULL){ fprintf(stderr, "cJSON_AddArrayToObject failed.\n"); cJSON_Delete(monitor); return NULL; } /*向resolutions数组对象中添加三个子条目*/ for(index=0; index<3; ++index){ /*1.创建JSON对象resolution*/ cJSON *resolution = cJSON_CreateObject(); /*2.向resolution对象中添加子条目("width": 1024)*/ if(cJSON_AddNumberToObject(resolution, "width", resolution_numbers[index][0]) == NULL){ fprintf(stderr, "cJSON_AddNumberToObject failed.\n"); cJSON_Delete(monitor); return NULL; } /*3.向resolution对象中添加子条目("height": 768)*/ if(cJSON_AddNumberToObject(resolution, "height", resolution_numbers[index][1]) == NULL){ fprintf(stderr, "cJSON_AddNumberToObject failed.\n"); cJSON_Delete(monitor); return NULL; } /*将resolution对象添加到数组resolutions中*/ cJSON_AddItemToArray(resolutions, resolution); } /*将JSON对象写入string对象中*/ string = cJSON_Print(monitor); if(string == NULL){ fprintf(stderr, "Failed to print monitor.\n"); cJSON_Delete(monitor); return NULL; } /*释放cJSON对象占用的资源*/ cJSON_Delete(monitor); return string; } int main() { char *string; string = create_JSON(); printf("string=\n%s\n", string); return 0; } |
编译、运行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[root@localhost demo]# gcc -Wall -g createJSON.c -o createJSON -lcjson [root@localhost demo]# ./createJSON string= { "name": "MIUI 4K", "resolutions": [{ "width": 1024, "height": 768 }, { "width": 1280, "height": 1024 }, { "width": 1920, "height": 1080 }] } |
(2)解析JSON
如下示例解析JSON串,并判断是否存在{“width”: 1920, “height”: 1080}的子条目,parseJSON.c代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
#include <stdio.h> #include <cjson/cJSON.h> const char* const monitor = "{\n\ \t\"name\":\t\"MIUI 4K\",\n\ \t\"resolutions\":\t[{\n\ \t\t\t\"width\":\t1024,\n\ \t\t\t\"height\":\t768\n\ \t\t}, {\n\ \t\t\t\"width\":\t1280,\n\ \t\t\t\"height\":\t1024\n\ \t\t}, {\n\ \t\t\t\"width\":\t1920,\n\ \t\t\t\"height\":\t1080\n\ \t\t}]\n\ }"; int parse_JSON(const char* const monitor) { const cJSON *resolution = NULL; const cJSON *resolutions = NULL; const cJSON *name = NULL; int ret = 0; cJSON *monitor_json = cJSON_Parse(monitor); if(monitor_json == NULL){ const char *error_ptr = cJSON_GetErrorPtr(); if(error_ptr != NULL){ fprintf(stderr, "Error before: %s\n", error_ptr); } ret = 0; cJSON_Delete(monitor_json); return ret; } name = cJSON_GetObjectItemCaseSensitive(monitor_json, "name"); if(cJSON_IsString(name) && (name->valuestring != NULL)){ printf("Checking monitor \"%s\"\n", name->valuestring); } resolutions = cJSON_GetObjectItemCaseSensitive(monitor_json, "resolutions"); cJSON_ArrayForEach(resolution, resolutions){ cJSON *width = cJSON_GetObjectItemCaseSensitive(resolution, "width"); cJSON *height = cJSON_GetObjectItemCaseSensitive(resolution, "height"); if(!cJSON_IsNumber(width) || !cJSON_IsNumber(height)){ ret = 0; cJSON_Delete(monitor_json); return ret; } printf("width->valuedouble=%lf, height->valuedouble=%lf\n", width->valuedouble, height->valuedouble); if((width->valuedouble == 1920) && (height->valuedouble == 1080)){ ret = 1; cJSON_Delete(monitor_json); return ret; } } cJSON_Delete(monitor_json); return ret; } int main() { int ret = 0; ret = parse_JSON(monitor); printf("ret = %d\n", ret); if(ret == 1){ printf("存在子条目:{\"width\": 1920, \"height\": 1080}\n"); } return 0; } |
编译、运行:
1 2 3 4 5 6 7 8 |
[root@localhost demo]# gcc -Wall -g parseJSON.c -o parseJSON -lcjson [root@localhost demo]# ./parseJSON Checking monitor "MIUI 4K" width->valuedouble=1024.000000, height->valuedouble=768.000000 width->valuedouble=1280.000000, height->valuedouble=1024.000000 width->valuedouble=1920.000000, height->valuedouble=1080.000000 ret = 1 存在子条目:{"width": 1920, "height": 1080} |
4.其他
本文主要介绍、总结了cJSON的一般使用方法,如需深入学习请参考文末所列的参考文献。转载请注明出处。
参考:
[1]百度百科词条“JSON”: https://baike.baidu.com/item/JSON/2462549?fr=aladdin
[2]http://www.json.org/json-zh.html
[3]https://en.wikipedia.org/wiki/JSON
[4] https://github.com/DaveGamble/cJSON
感谢翻译~