概述
cJSON文档解析
cJSON是一个轻量级且易于扩展的JSON解析开源库。
github地址为:https://github.com/DaveGamble/cJSON
-
安装与使用
-
源码安装:将cJSON.c与cJSON.h这两个文件复制入自己的工程项目中,通过
#include "cJSON.h"
就可以使用了 -
编译安装
-
#克隆cJSON库的源码 git clone https://github.com/DaveGamble/cJSON cd cJSON mkdir build cd build cmake .. #编译源码 make #将头文件放入/usr/local/include/cjson文件夹中,库放入/usr/local/lib,需要超级用户的权限 sudo make install
-
通过
#include <cjson/cJSON.h>
进行使用
-
-
-
数据结构
cJSON使用
cJSON
结构体来存储JSON数据/* cJSON结构 */ typedef struct cJSON { struct cJSON *next; struct cJSON *prev; struct cJSON *child; int type; char *valuestring; //不应该直接向valueint写数据,而是使用cJSON_SetNumberValue函数进行赋值 int valueint; double valuedouble; char *string; } cJSON;
- 一个
cJSON
结构体存储一个JSON的值。cJSON
结构体中的type
是指向JSON值的类型,同时是以bit-flag
的形式存储,这意味着不能仅仅通过比较type
的值来判断JSON值的类型。 - 可以使用
cJSON_Is...
函数来检查cJSON
结构体存储JSON的值的类型,它会对空指针进行检查,同时返回一个布尔值来判断否是该值。 cJSON
可能的值有:cJSON_Invalid
:无效值,没有存储任何的值。当成员全部清零时便是该值cJSON_False
:为假的布尔值cJSON_True
:为真的布尔值cJSON_NULL
:空值cJOSN_Number
:数值,既作为双精度浮点数存储于valuedouble
,又作为整型存储于valueint
。如果数值超过了整型的范围,valueint
将被赋值为INT_MAX
或者INT_MIN
cJSON_String
:字符串,以’ ’的形式结尾,存储于valuestring
cJSON_Array
:数组,通过cJSON
节点链表来存储array值,每一个元素使用next
和prev
进行相互连接,第一个成员的prev
值为NULL,最后一个成员的next
值为NULL。同时使用成员child
指针来指向该链表cJSON_Object
:对象,与数组有相似的存储方式,唯一不同的是对象会将键值放入成员string
中cJSON_Raw
:JSON格式的字符串,以’ ’结尾,存储于valuestring
。在一次又一次的打印相同的JSON场景下,它能够节省内存。cJSON不会在解析json格式的字符串时产生这种类型。值的注意的是cJSON库不会检查其值是否是合法的cJSON_IsReference
:成员child
指针或者valuestring
指向的节点并不属于自己,自己仅仅是一个引用。因此,cJSON_Delete
和其他相关的函数只会释放这个引用本身,而不会去释放child
或者valuestring
cJSON_StringIsConst
:成员string
指向的是一个字面量,因此,cJSON_Delete
和其他相关的函数不会试图去释放string
的内存
- 一个
-
使用这些数据结构
对于每一种类型的值都有一个对应的函数
cJSON_Create...
来创建。这类函数会动态分配一个cJSON
的结构,所以需要在使用完后用cJSON_Delete
释放掉内存,以避免内存泄漏。注意:当你已经把一个节点加入了一个数组或者对象,你就不能在用
cJSON_Delete
去释放这个节点的内存了,当该数组或者对象被删除时,这个节点也会被删除-
创建基本类型的函数
null
:cJSON_CreateNull
booleans
:cJSON_CreateTrue
,cJSON_CreateFalse
或者cJSON_CreateBool
numbers
:cJSON_CreateNumber
strings
:cJSON_CreateString
-
数组
你可以使用
cJSON_CreateArray
函数来创建一个新的空数组。cJSON_CreateArrayReference
函数可以创建一个数组的引用,因为它没有属于自己的内容,所以它的子元素不会被cJSON_Delete
给删除使用
cJSON_AddItemToArray
函数可以在数组的最后增加元素。使用cJSON_AddItemReferenceToArray
函数将会增加一个元素去引用其他的节点,这就意味着cJSON_Delete
不会去删除这个元素的child
或者valuestring
属性,因此当这些属性在其他地方使用的时候,不用担心重复释放内存的事情发生。使用cJSON_InsertItemInArray
函数可以将一个新元素插入数组中的0索引的位置,旧的元素的索引依次加1如果你想根据索引去移除数组中的一个元素并且继续去使用它,可以使用
cJSON_DetachItemFromArray
函数,它将会返回被分离的数组。为避免内存泄漏,确保将返回值赋值给一个指针当你需要替换数组中的某一个元素时,
cJSON_ReplaceItemInArray
函数使用索引的方式来进行替换。cJSON_ReplaceItemViaPointer
函数使用指向该元素的指针,同时如果失败则会返回0。这两个函数会分离出旧的元素并删除它,同时在这个位置加入新的元素使用
cJSON_GetArraySize
函数得到数组的大小。使用cJSON_GetArrayItem
得到一个元素的索引因为数组是以链表的方式进行存储的,所以通过索引的方式进行遍历效率是很低的( O(n^2) )。建议使用宏
cJSON_ArrayForEach
来遍历数组,它具有时间复杂度为( O(n) ) -
对象
你可以使用
cJSON_CreateObject
函数来创建一个新的空对象。cJSON_CreateObjectReference
函数可以创建一个对象的引用,因为它没有属于自己的内容,所以它的子元素不会被cJSON_Delete
给删除使用
cJSON_AddItemToObject
函数来增加一个元素到对象里。使用cJSON_AddItemToObjectCS
函数来增加对象里的元素时,使用的键值(结构体cJSON
中string
成员)是一个引用或者是字面量,因此它会被cJSON_Delete
给忽略。使用cJSON_AddItemReferenceToArray
函数将会增加一个元素去引用其他的节点,这就意味着cJSON_Delete
不会去删除这个元素的child
或者valuestring
属性,因此当这些属性在其他地方使用的时候,不用担心重复释放内存的事情发生使用
cJSON_DetachItemFromObjectCaseSensitive
函数来从对象中分离出一个元素,从函数命名可以看出对于指定的键值是大小写敏感的,它将会返回被分离的数组。为避免内存泄漏,确保将返回值赋值给一个指针使用
cJSON_DeleteItemFromObjectCaseSensitive
函数来从一个对象中删除一个元素,可以把它看成先从对象中分离出该元素然后在删除当你需要替换对象中的某一个元素时,
cJSON_ReplaceItemInObjectCaseSensitive
函数使用j键值查找的方式来进行替换。cJSON_ReplaceItemViaPointer
函数使用指向该元素的指针来查找并替换,同时如果失败则会返回0。这两个函数会分离出旧的元素并删除它,同时在这个位置加入新的元素因为对象的存储方式和数组很像,所以同样可以通过
cJSON_GetArraySize
来得到对象里元素的个数使用
cJSON_GetObjectItemCaseSensitive
来访问对象中的某一个元素使用宏
cJSON_ArrayForEach
来遍历一个对象cJSON
同样也提供便利的工具函数来快速的在对象内部创建一个新的元素,比如说cJSON_AddNullToObject
函数将会返回新加的元素指针,如果失败则返回NULL
-
-
解析JSON字符串
可以使用
cJSON_Parse
函数来一些以’ ’结尾的字符串进行解析cJSON *json = cJSON_Parse(string);
解析后的结果是
cJSON
的树状的数据结构,一旦解析成功,就有责任在使用完后使用cJSON_Delete
释放内存默认分配内存使用的是
malloc
函数,释放内存使用的是free
函数。但是可以使用cJSON_InitHooks
函数来全局性改变当一个错误发生时,
cJSON_GetErrorPtr
函数可以得到指向输入字符串中错误的位置的指针。值的注意的是在多线程的情况下,该函数会产生竞争条件,更好的方法是使用带有return_parse_end
参数的cJSON_ParseWithOpts
函数。如果你想有更多的选项,使用
cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
函数,return_parse_end
返回输入的JSON字符串的结尾或者一个错误发生的地方(从而在保障线程安全的情况下替换cJSON_GetErrorPtr
函数)。require_null_terminated
如果该值设为1,那么当输入的字符串在有效的以’ ’结尾的json字符串后还包含其他的数据,就会报错 -
打印JSON
使用
cJSON_Print
函数将一个cJSON
数据结构打印为字符串char *string = cJSON_Print(json);
该函数将会动态分配内存给一个字符串,将JSON表达式放入其中。一旦该函数返回,就有责任释放该内存(默认是
free
,取决于设置的cJSON_InitHooks
)cJSON_Print
将会用空白符来格式化JSON字符串。可以使用cJSON_PrintUnformatted
来无格式化的打印如果你关于返回的结果的字符串的大小有一个想法,你可以使用
cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
函数。fmt
是一个决定是否用空白字符格式化JSON字符串,prebuffer
指出了所用的第一个缓冲区大小。cJOSN_Print
当前使用256字节的缓冲区大小。一旦打印超过了大小,新的缓冲区会被动态分配,在继续打印之前旧的缓冲区里的内容复制到新的缓冲区里。使用
cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
函数可以完全避免动态的内存分配,该函数需要指向缓冲区的指针和该缓冲区的大小,如果缓冲区过小,打印将会失败,函数返回0。一旦成功,函数返回1。值得注意的是需要准备超过实际需要的字节还要多5个字节,因为cJSON并不是100%的精确估计提供的内存是否足够 -
示例
-
在这个例子里,我们想去构建一个JSON并解析它
{ "name": "Awesome 4K", "resolutions": [ { "width": 1280, "height": 720 }, { "width": 1920, "height": 1080 }, { "width": 3840, "height": 2160 } ] }
-
构建以上的json,然后打印成字符串
//create a monitor with a list of supported resolutions char* create_monitor(void) { const unsigned int resolution_numbers[3][2] = { {1280, 720}, {1920, 1080}, {3840, 2160} }; char *string = NULL; cJSON *name = NULL; cJSON *resolutions = NULL; cJSON *resolution = NULL; cJSON *width = NULL; cJSON *height = NULL; size_t index = 0; cJSON *monitor = cJSON_CreateObject(); if (monitor == NULL) { goto end; } name = cJSON_CreateString("Awesome 4K"); if (name == NULL) { goto end; } /* after creation was successful, immediately add it to the monitor, * thereby transfering ownership of the pointer to it */ cJSON_AddItemToObject(monitor, "name", name); resolutions = cJSON_CreateArray(); if (resolutions == NULL) { goto end; } cJSON_AddItemToObject(monitor, "resolutions", resolutions); for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index) { resolution = cJSON_CreateObject(); if (resolution == NULL) { goto end; } cJSON_AddItemToArray(resolutions, resolution); width = cJSON_CreateNumber(resolution_numbers[index][0]); if (width == NULL) { goto end; } cJSON_AddItemToObject(resolution, "width", width); height = cJSON_CreateNumber(resolution_numbers[index][1]); if (height == NULL) { goto end; } cJSON_AddItemToObject(resolution, "height", height); } string = cJSON_Print(monitor); if (string == NULL) { fprintf(stderr, "Failed to print monitor.n"); } end: cJSON_Delete(monitor); return string; }
-
我们可以使用
cJSON_Add...ToObject
辅助函数来更方便构建char *create_monitor_with_helpers(void) { const unsigned int resolution_numbers[3][2] = { {1280, 720}, {1920, 1080}, {3840, 2160} }; char *string = NULL; cJSON *resolutions = NULL; size_t index = 0; cJSON *monitor = cJSON_CreateObject(); if (cJSON_AddStringToObject(monitor, "name", "Awesome 4K") == NULL) { goto end; } resolutions = cJSON_AddArrayToObject(monitor, "resolutions"); if (resolutions == NULL) { goto end; } for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index) { cJSON *resolution = cJSON_CreateObject(); if (cJSON_AddNumberToObject(resolution, "width", resolution_numbers[index][0]) == NULL) { goto end; } if(cJSON_AddNumberToObject(resolution, "height", resolution_numbers[index][1]) == NULL) { goto end; } cJSON_AddItemToArray(resolutions, resolution); } string = cJSON_Print(monitor); if (string == NULL) { fprintf(stderr, "Failed to print monitor.n"); } end: cJSON_Delete(monitor); return string; }
-
解析并测试
/* return 1 if the monitor supports full hd, 0 otherwise */ int supports_full_hd(const char * const monitor) { const cJSON *resolution = NULL; const cJSON *resolutions = NULL; const cJSON *name = NULL; int status = 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: %sn", error_ptr); } status = 0; goto end; } 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)) { status = 0; goto end; } if ((width->valuedouble == 1920) && (height->valuedouble == 1080)) { status = 1; goto end; } } end: cJSON_Delete(monitor_json); return status; }
- 值的注意的是,对于
cJSON_Parse
函数的返回结果没有空指针的检查,因为cJSON_GetObjectItemCaseSensitive
对于输入空指针有检查
- 值的注意的是,对于
-
最后
以上就是内向水蜜桃为你收集整理的cJSON文档解析cJSON文档解析的全部内容,希望文章能够帮你解决cJSON文档解析cJSON文档解析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复