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. 關於對多線程的支持,可通過代碼設計規范來實現。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM