cJSON使用说明

一、包含头文件

二、数据结构

 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);
  1. 基本类型
    1. 使用cJSON_CreateNull()创建null。
    2. 使用cJSON_CreateTrue(),cJSON_CreateFalse()或cJSON_CreateBool()创建布尔值。
    3. 使用cJSON_CreateNumber()创建数字这将设置valuedouble和valueint。INT_MAX或INT_MIN是valueint值的上下限。
    4. 使用cJSON_CreateString()(字符创拷贝)创建字符串,或cJSON_CreateStringReference(直接指向字符串。意味着valuestring不能直接被删除,你需要负责该对象的整个生命期)
  2. 数组
    1. 您可以使用创建一个空数组cJSON_CreateArray。cJSON_CreateArrayReference可用于创建不“拥有”其内容的数组,因此其内容不会被删除cJSON_Delete。
    2. cJSON_AddItemToArray()添加到末尾。cJSON_AddItemReferenceToArray(),将一个元素作为一个指向一个item、数组或字符串的引用。这意味着cJSON_Delete不会删除该项child或valuestring属性,因此如果已经在其他地方使用过,则不会发生双重释放。要在中间插入项目,请使用cJSON_InsertItemInArray。它将在给定的基于0的索引处插入项目,并将所有现有项目向右移动。
    3. 如果你想从一个给定索引的数组中取出一个项目并继续使用它,使用cJSON_DetachItemFromArray它将返回分离的项目,所以一定要把它分配给一个指针,否则你会有内存泄漏。
    4. cJSON_DeleteItemFromArray()完成删除条目。工作类似于cJSON_DetachItemFromArray,删除的条目会被cJSON_Delete删除。
    5. 删除条目,cJSON_ReplaceItemInArray通过下标删除,cJSON_ReplaceItemViaPointer通过指针删除。如果失败,cJSON_ReplaceItemViaPointer会返回0。cJSON_ReplaceItemViaPointer会分离item并删除它,然后在它的位置插入新的item.
    6. 使用cJSON_GetArraySize获取数组大小。使用cJSON_GetArrayItem获取一个元素,通过一个给定的下标。
    7. 因为数组是以链表存储的,通过下标进行迭代是低效的(O(n2)),因此可以使用宏cJSON_ArrayForEach来对一个数组进行迭代,其时间复杂度为O(n)。
  3. 对象
    1. 您可以使用创建一个空对象cJSON_CreateObject。cJSON_CreateObjectReference可用于创建不“拥有”其内容的对象,因此其内容不会被删除cJSON_Delete。
    2. 使用cJSON_AddItemToObject为对象增加一个item。使用cJSON_AddItemToObjectCS将一个常量或引用(JSON结构中的item键值,字符串)作为item添加给对象,因此其不会通过cJSON_delete删除。使用cJSON_AddItemReferenceToArray,一个元素会被作为其他对象、数组或字符串的引用添加。这意味着cJSON_Delete不会删除该item的child或valuestring属性。因此不用担心双重释放。
    3. 使用cJSON_DetachItemFromObjectCaseSensitive从一个对象中删除一个item。他会分离item,因此要保证分配一个指针,否则会内存泄漏。
    4. cJSON_DeleteItemFromObjectCaseSensitive会完成删除item。他工作类似于cJSON_DetachItemFromObjectCaseSensitive后调用了cJSON_Delte。
    5. cJSON_ReplaceItemInObjectCaseSensitive使用一个键值,cJSON_ReplaceItemViaPointer使用一个指向元素的指针,删除对象中的item。如果失败,cJSON_ReplaceItemViaPointer返回0。内部原理是分离旧的item,删除它,返回在它的位置插入新的item。
    6. 通过cJSON_GetArraySize获取对象的size。他能生效,是因为,对象在内部被存储为数组。
    7. 使用cJSON_GetObjectItemCaseSensitive访问对象中的item。
    8. 要迭代对象,你可像数组一样使用宏cJSON_ArrayForEach
    9. 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。

七、注意事项

  1. 零字符
    1. cJSON不支持字符串包含零字符,如‘\0'或\u0000。这是不可能出现的,因为字符串是以0结尾的。
  2. 字符编码
    1. cJSON只支持UTF-8编码输入。在大多数情况下,它不会拒绝无效的UTF-8,它只是按原样传递。只要输入不包含无效的UTF-8,输出将始终是有效的UTF-8。
  3. C标准
    1. cJSON是使用ANSI C(or C89, C90)编写的。如果你的编译器和C库不遵循此标准,将无法确保正确的行为。
    2. ANSI C不是C++,因此它不应被C++编译器编译。你可以使用C编译器编译它,然后链接到你的C++代码。尽管C++编译的代码能工作,但无法保证正确的行为。
  4. 浮点数
    1. cJSON不正式支持doubleIEEE754双精度浮点数以外的任何实现。它可能仍然适用于其他实现,但这些错误将被视为无效

    2. cJSON支持的浮点文字的最大长度目前为63个字符。
  5. 数组和对象的深度嵌套
    1. cJSON不支持嵌套太深的数组和对象,因为这会导致堆栈溢出。为了防止此cJSON默认限制深度CJSON_NESTING_LIMIT为1000,但可以在编译时更改。
  6. 线程安全

  通常,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但总是只返回第一个。

 

备注:

  1. 该文来自cJSON项目官方文档中部分内容,中文翻译参考谷歌翻译。https://github.com/DaveGamble/cJSON
  2. 官方推荐使用cmake来构建整个项目。可用make install,默认安装头/usr/local/include/cjson和库/usr/local/lib 
  3. 官方不推荐使用Makefile。运行make all,make install安装路径头/usr/local/include/cjson和库中的/usr/local/lib。可以通过设置PREFIX和DESTDIR变量来更改:make PREFIX=/usr DESTDIR=temp install。
  4. 关于对多线程的支持,可通过代码设计规范来实现。

 

posted on 2018-09-18 14:38  mofei004  阅读(12516)  评论(0编辑  收藏  举报

导航