一、包含头文件
二、数据结构
1 #include <cjson/cJSON.h> 2 3 /* The cJSON structure: */ 4 typedef struct cJSON 5 { 6 struct cJSON *next; 7 struct cJSON *prev; 8 struct cJSON *child; 9 int type; 10 char *valuestring; 11 /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ 12 int valueint; 13 double valuedouble; 14 char *string; 15 } cJSON;
type:说明了JSON值的类型,位标识。
通过以下方法检查元素类型:
1 /* These functions check the type of an item */ 2 CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); 3 CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); 4 CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); 5 CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); 6 CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); 7 CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); 8 CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); 9 CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); 10 CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); 11 CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
type的取值:
1 /* cJSON Types: */ 2 #define cJSON_Invalid (0) 3 #define cJSON_False (1 << 0) 4 #define cJSON_True (1 << 1) 5 #define cJSON_NULL (1 << 2) 6 #define cJSON_Number (1 << 3) 7 #define cJSON_String (1 << 4) 8 #define cJSON_Array (1 << 5) 9 #define cJSON_Object (1 << 6) 10 #define cJSON_Raw (1 << 7) /* raw json */ 11 12 #define cJSON_IsReference 256 13 #define cJSON_StringIsConst 512
cJSON_IsReference:表明子节点指向的条目或值串(valuestring)不属于此项目,它只是一个引用。cJSON_Delete和其他函数只会解除分配这个项目,而不是children/valuestring。
cJSON_StringIsConst:表示该string指向一个常量string。表明cJSON_Delete和其他函数不能尝试释放该string。
三、使用数据结构
1 /* These calls create a cJSON item of the appropriate type. */ 2 CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); 3 CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); 4 CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); 5 CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); 6 CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); 7 CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); 8 /* raw json */ 9 CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); 10 CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); 11 CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); 12 13 /* Create a string where valuestring references a string so 14 * it will not be freed by cJSON_Delete */ 15 CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); 16 /* Create an object/arrray that only references it's elements so 17 * they will not be freed by cJSON_Delete */ 18 CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); 19 CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); 20 21 /* These utilities create an Array of count items. */ 22 CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); 23 CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); 24 CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); 25 CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count);
- 基本类型
- 使用cJSON_CreateNull()创建null。
- 使用cJSON_CreateTrue(),cJSON_CreateFalse()或cJSON_CreateBool()创建布尔值。
- 使用cJSON_CreateNumber()创建数字这将设置valuedouble和valueint。INT_MAX或INT_MIN是valueint值的上下限。
- 使用cJSON_CreateString()(字符创拷贝)创建字符串,或cJSON_CreateStringReference(直接指向字符串。意味着valuestring不能直接被删除,你需要负责该对象的整个生命期)
- 数组
- 您可以使用创建一个空数组cJSON_CreateArray。cJSON_CreateArrayReference可用于创建不“拥有”其内容的数组,因此其内容不会被删除cJSON_Delete。
- cJSON_AddItemToArray()添加到末尾。cJSON_AddItemReferenceToArray(),将一个元素作为一个指向一个item、数组或字符串的引用。这意味着cJSON_Delete不会删除该项child或valuestring属性,因此如果已经在其他地方使用过,则不会发生双重释放。要在中间插入项目,请使用cJSON_InsertItemInArray。它将在给定的基于0的索引处插入项目,并将所有现有项目向右移动。
- 如果你想从一个给定索引的数组中取出一个项目并继续使用它,使用cJSON_DetachItemFromArray它将返回分离的项目,所以一定要把它分配给一个指针,否则你会有内存泄漏。
- cJSON_DeleteItemFromArray()完成删除条目。工作类似于cJSON_DetachItemFromArray,删除的条目会被cJSON_Delete删除。
- 删除条目,cJSON_ReplaceItemInArray通过下标删除,cJSON_ReplaceItemViaPointer通过指针删除。如果失败,cJSON_ReplaceItemViaPointer会返回0。cJSON_ReplaceItemViaPointer会分离item并删除它,然后在它的位置插入新的item.
- 使用cJSON_GetArraySize获取数组大小。使用cJSON_GetArrayItem获取一个元素,通过一个给定的下标。
- 因为数组是以链表存储的,通过下标进行迭代是低效的(O(n2)),因此可以使用宏
cJSON_ArrayForEach来对一个数组进行迭代,其时间复杂度为O(n)。
- 对象
- 您可以使用创建一个空对象cJSON_CreateObject。cJSON_CreateObjectReference可用于创建不“拥有”其内容的对象,因此其内容不会被删除cJSON_Delete。
- 使用cJSON_AddItemToObject为对象增加一个item。使用
cJSON_AddItemToObjectCS将一个常量或引用(JSON结构中的item键值,字符串)作为item添加给对象,因此其不会通过cJSON_delete删除。使用
cJSON_AddItemReferenceToArray,一个元素会被作为其他对象、数组或字符串的引用添加。这意味着cJSON_Delete不会删除该item的child或valuestring属性。因此不用担心双重释放。
- 使用cJSON_DetachItemFromObjectCaseSensitive从一个对象中删除一个item。他会分离item,因此要保证分配一个指针,否则会内存泄漏。
- cJSON_DeleteItemFromObjectCaseSensitive会完成删除item。他工作类似于
cJSON_DetachItemFromObjectCaseSensitive后调用了cJSON_Delte。
cJSON_ReplaceItemInObjectCaseSensitive使用一个键值,
cJSON_ReplaceItemViaPointer使用一个指向元素的指针,删除对象中的item。如果失败,cJSON_ReplaceItemViaPointer返回0。内部原理是分离旧的item,删除它,返回在它的位置插入新的item。
- 通过cJSON_GetArraySize获取对象的size。他能生效,是因为,对象在内部被存储为数组。
- 使用cJSON_GetObjectItemCaseSensitive访问对象中的item。
- 要迭代对象,你可像数组一样使用宏
cJSON_ArrayForEach
- cJSON提供了很多方便的帮助函数,能快速的创建一个item,并将其添加到对象,如cJSON_AddNullToObject。他们返回指向新item的指针或者null。
四、解析JSON
对于给定包含0终止符的字符串,你可使用cJSON_Parse解析它:
1 cJSON *json = cJSON_Parse(string);
它解析JSON,并分配一个cJSON树来表示它。一旦返回,你完全有责任在使用后调用cJSON_Delete。
通过所使用的分配器cJSON_Parse默认使用malloc和free,但可以用cJSON_InitHooks改变(全局)。
当一个错误发生,使用cJSON_GetErrorPtr可以访问,指向输入字符串的错误位置的指针。(原文:If an error occurs a pointer to the position of the error in the input string can be accessed using cJSON_GetErrorPtr. )请注意,这可以产生多线程情况下的竞争条件,在这种情况下,最好是使用cJSON_ParseWithOpts
带有return_parse_end
。默认情况下,输入字符串中跟随解析的JSON的字符不会被视为错误。
如果您想要更多选项,请使用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之后的数据时使其成为错误。
注:cJSON_InitHooks和cJSON_GetErrorPtr是cjson线程不安全的几个原因之二。
五、打印JSON
给定一个CJSON树,可以使用cJSON_Print打印:
1 char *string = cJSON_Print(json);
它将分配一个字符串并将树的JSON表示形式打印到其中。一旦它返回,您完全有责任在使用分配器后解除分配。(通常free取决于设定的内容cJSON_InitHooks)。
cJSON_Print将使用空格打印以进行格式化。如果要打印而不进行格式化,请使用cJSON_PrintUnformatted。
如果您大致了解结果字符串的大小,可以使用cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)。fmt是一个布尔值,用于打开和关闭空格的格式。prebuffer指定用于打印的第一个缓冲区大小。cJSON_Print目前使用256个字节作为它的第一个缓冲区大小。一旦打印空间不足,就会分配一个新的缓冲区,并在继续打印之前复制旧的缓冲区。
使用可以完全避免这些动态缓冲区分配cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)。它需要一个缓冲区来指向要打印的指针和它的长度。如果达到长度,打印将失败并返回0。如果成功,1则返回。请注意,您应该提供比实际需要多5个字节,因为cJSON在估计提供的内存是否足够时不是100%准确。
六、例子
在例子中,我们要建立并解析下面的JSON:
1 { 2 "name": "Awesome 4K", 3 "resolutions": [ 4 { 5 "width": 1280, 6 "height": 720 7 }, 8 { 9 "width": 1920, 10 "height": 1080 11 }, 12 { 13 "width": 3840, 14 "height": 2160 15 } 16 ] 17 }
打印
建立上面的JSON并打印为字符串。
1 //create a monitor with a list of supported resolutions 2 char* create_monitor(void) 3 { 4 const unsigned int resolution_numbers[3][2] = { 5 {1280, 720}, 6 {1920, 1080}, 7 {3840, 2160} 8 }; 9 char *string = NULL; 10 cJSON *name = NULL; 11 cJSON *resolutions = NULL; 12 cJSON *resolution = NULL; 13 cJSON *width = NULL; 14 cJSON *height = NULL; 15 size_t index = 0; 16 17 cJSON *monitor = cJSON_CreateObject(); 18 if (monitor == NULL) 19 { 20 goto end; 21 } 22 23 name = cJSON_CreateString("Awesome 4K"); 24 if (name == NULL) 25 { 26 goto end; 27 } 28 /* after creation was successful, immediately add it to the monitor, 29 * thereby transfering ownership of the pointer to it */ 30 cJSON_AddItemToObject(monitor, "name", name); 31 32 resolutions = cJSON_CreateArray(); 33 if (resolutions == NULL) 34 { 35 goto end; 36 } 37 cJSON_AddItemToObject(monitor, "resolutions", resolutions); 38 39 for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index) 40 { 41 resolution = cJSON_CreateObject(); 42 if (resolution == NULL) 43 { 44 goto end; 45 } 46 cJSON_AddItemToArray(resolutions, resolution); 47 48 width = cJSON_CreateNumber(resolution_numbers[index][0]); 49 if (width == NULL) 50 { 51 goto end; 52 } 53 cJSON_AddItemToObject(resolution, "width", width); 54 55 height = cJSON_CreateNumber(resolution_numbers[index][1]); 56 if (height == NULL) 57 { 58 goto end; 59 } 60 cJSON_AddItemToObject(resolution, "height", height); 61 } 62 63 string = cJSON_Print(monitor); 64 if (string == NULL) 65 { 66 fprintf(stderr, "Failed to print monitor.\n"); 67 } 68 69 end: 70 cJSON_Delete(monitor); 71 return string; 72 }
另外,我们使用cJSON_Add...ToObject帮助函数使我们的生活更轻松(^-^):
1 char *create_monitor_with_helpers(void) 2 { 3 const unsigned int resolution_numbers[3][2] = { 4 {1280, 720}, 5 {1920, 1080}, 6 {3840, 2160} 7 }; 8 char *string = NULL; 9 cJSON *resolutions = NULL; 10 size_t index = 0; 11 12 cJSON *monitor = cJSON_CreateObject(); 13 14 if (cJSON_AddStringToObject(monitor, "name", "Awesome 4K") == NULL) 15 { 16 goto end; 17 } 18 19 resolutions = cJSON_AddArrayToObject(monitor, "resolutions"); 20 if (resolutions == NULL) 21 { 22 goto end; 23 } 24 25 for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index) 26 { 27 cJSON *resolution = cJSON_CreateObject(); 28 29 if (cJSON_AddNumberToObject(resolution, "width", resolution_numbers[index][0]) == NULL) 30 { 31 goto end; 32 } 33 34 if(cJSON_AddNumberToObject(resolution, "height", resolution_numbers[index][1]) == NULL) 35 { 36 goto end; 37 } 38 39 cJSON_AddItemToArray(resolutions, resolution); 40 } 41 42 string = cJSON_Print(monitor); 43 if (string == NULL) { 44 fprintf(stderr, "Failed to print monitor.\n"); 45 } 46 47 end: 48 cJSON_Delete(monitor); 49 return string; 50 }
解析
在此例中,我们会以上面的格式解析一个JSON,并在打印诊断输出时检查monitor是否为一个full HD resolution。
1 /* return 1 if the monitor supports full hd, 0 otherwise */ 2 int supports_full_hd(const char * const monitor) 3 { 4 const cJSON *resolution = NULL; 5 const cJSON *resolutions = NULL; 6 const cJSON *name = NULL; 7 int status = 0; 8 cJSON *monitor_json = cJSON_Parse(monitor); 9 if (monitor_json == NULL) 10 { 11 const char *error_ptr = cJSON_GetErrorPtr(); 12 if (error_ptr != NULL) 13 { 14 fprintf(stderr, "Error before: %s\n", error_ptr); 15 } 16 status = 0; 17 goto end; 18 } 19 20 name = cJSON_GetObjectItemCaseSensitive(monitor_json, "name"); 21 if (cJSON_IsString(name) && (name->valuestring != NULL)) 22 { 23 printf("Checking monitor \"%s\"\n", name->valuestring); 24 } 25 26 resolutions = cJSON_GetObjectItemCaseSensitive(monitor_json, "resolutions"); 27 cJSON_ArrayForEach(resolution, resolutions) 28 { 29 cJSON *width = cJSON_GetObjectItemCaseSensitive(resolution, "width"); 30 cJSON *height = cJSON_GetObjectItemCaseSensitive(resolution, "height"); 31 32 if (!cJSON_IsNumber(width) || !cJSON_IsNumber(height)) 33 { 34 status = 0; 35 goto end; 36 } 37 38 if ((width->valuedouble == 1920) && (height->valuedouble == 1080)) 39 { 40 status = 1; 41 goto end; 42 } 43 } 44 45 end: 46 cJSON_Delete(monitor_json); 47 return status; 48 }
注意cJSON_Parse的结果没有检查NULL,因为cJSON_GetObjectItemCaseSensitive已经检查过。因此如果输入为NULL,NULL值会被传递,cJSON_IsNumber
和cJSON_IsString返回0。
七、注意事项
- 零字符
- cJSON不支持字符串包含零字符,如‘\0'或\u0000。这是不可能出现的,因为字符串是以0结尾的。
- 字符编码
- cJSON只支持UTF-8编码输入。在大多数情况下,它不会拒绝无效的UTF-8,它只是按原样传递。只要输入不包含无效的UTF-8,输出将始终是有效的UTF-8。
- C标准
- cJSON是使用ANSI C(or C89, C90)编写的。如果你的编译器和C库不遵循此标准,将无法确保正确的行为。
- ANSI C不是C++,因此它不应被C++编译器编译。你可以使用C编译器编译它,然后链接到你的C++代码。尽管C++编译的代码能工作,但无法保证正确的行为。
- 浮点数
-
cJSON不正式支持doubleIEEE754双精度浮点数以外的任何实现。它可能仍然适用于其他实现,但这些错误将被视为无效
- cJSON支持的浮点文字的最大长度目前为63个字符。
-
- 数组和对象的深度嵌套
- cJSON不支持嵌套太深的数组和对象,因为这会导致堆栈溢出。为了防止此cJSON默认限制深度
CJSON_NESTING_LIMIT
为1000,但可以在编译时更改。
- cJSON不支持嵌套太深的数组和对象,因为这会导致堆栈溢出。为了防止此cJSON默认限制深度
- 线程安全
通常,cJSON不是线程安全的。但在以下情况下是线程安全的。
1 cJSON_GetErrorPtr 没有被调用过。可使用cJSON_ParseWithOpts 的参数return_parse_end 替代。
2 cJSON_InitHooks 仅仅在任何线程使用cJSON之前调用。
3 setlocale不在所有的cJSON函数返回前被调用。
7.区分大小写
最初创建cJSON时,它未遵循JSON标准,并未区分大小写字母。如果您需要正确的,符合标准的行为,则需要使用CaseSensitive
可用的功能。
8.重复对象成员
cJSON支持解析和打印包含具有多个具有相同名称的成员的对象的JSON。cJSON_GetObjectItemCaseSensitive
但总是只返回第一个。
备注:
- 该文来自cJSON项目官方文档中部分内容,中文翻译参考谷歌翻译。https://github.com/DaveGamble/cJSON
- 官方推荐使用cmake来构建整个项目。可用make install,默认安装头/usr/local/include/cjson和库/usr/local/lib
- 官方不推荐使用Makefile。运行make all,make install安装路径头/usr/local/include/cjson和库中的/usr/local/lib。可以通过设置PREFIX和DESTDIR变量来更改:make PREFIX=/usr DESTDIR=temp install。
- 关于对多线程的支持,可通过代码设计规范来实现。