cjson介紹-應用-實例-源碼分析

1.json簡介

        JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。 易於人閱讀和編寫。同時也易於機器解析和生成。 它基於JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集。 JSON採用完全獨立於語言的文本格式,但是也使用了類似於C語言家族的習慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 這些特性使JSON成爲理想的數據交換語言。
        想要進一步瞭解,可以參考 json 的官方介紹,它是英文版的,亦可參考中文翻譯版本.。
        同時,可參考xml語言,進行對比,此處不作詳細說明,有興趣的朋友可以參考網上文章。比如:https://www.cnblogs.com/yank/p/4028266.html

2.cjson介紹

            cjson 的代碼只有 1000+ 行, 而且只是簡單的幾個函數的調用,使用方便,可參考https://sourceforge.net/projects/cjson/
詳細介紹。另:通過閱讀sourceforge下載的源碼資料中Readme,可掌握cjson使用方法及應用。

3.cjson應用實例

3.0.實例源碼下載

        本人使用codeblocks編譯,運行ubuntu14.04,codeblocks13,實例借鑑他人代碼,後面會列出鏈接,並在此表示感謝。

3.1.json標準格式

標準JSON的合法符號:{(左大括號)  }(右大括號)  "(雙引號)  :(冒號)  ,(逗號)  [(左中括號)  ](右中括號)

JSON字符串:特殊字符可在字符前面加 \ 或使用 \u 加 4位16進制數來處理

[html] view plain copy
  1. {"name":"jobs"}  

JSON布爾:必須小寫的true和false

[html] view plain copy
  1. {"bool":true}  

JSON空:必須小寫的null

[html] view plain copy
  1. {"object":null}  

JSON數值:不能使用8/16進制

[html] view plain copy
  1. {"num":60}  
  2. {"num":-60}  
  3. {"num":6.6666}  
  4. {"num":1e+6}<!-- 1乘10的6次方,e不區分大小寫 -->  
  5. {"num":1e-6}<!-- 1乘10的負6次方,e不區分大小寫 -->  

JSON對象

[html] view plain copy
  1. {  
  2.     "starcraft": {  
  3.         "INC": "Blizzard",  
  4.         "price": 60  
  5.     }  
  6. }  

JSON數組

[html] view plain copy
  1. {  
  2.     "person": [  
  3.         "jobs",  
  4.         60  
  5.     ]  
  6. }  

JSON對象數組

[html] view plain copy
  1. {  
  2.     "array": [  
  3.         {  
  4.             "name": "jobs"  
  5.         },  
  6.         {  
  7.             "name": "bill",  
  8.             "age": 60  
  9.         },  
  10.         {  
  11.             "product": "war3",  
  12.             "type": "game",  
  13.             "popular": true,  
  14.             "price": 60  
  15.         }  
  16.     ]  
  17. }  

參考:http://www.json.org/json-zh.html

3.2.使用cJSON創建JSON字符串

    在Linux下,使用C語言編程,開始JSON字符串的創建。我們還是一步步來,逐漸由簡單到複製。  

1,下載源碼

可以從如下網站來下載:https://sourceforge.net/projects/cjson/ 。

2,包含cJSON的源碼

下載下來,解壓後,從裏面找到兩個文件(cJSON.c、cJSON.h),複製到我們的工程裏面。只需在函數中包含頭文件(#include “cJSON.h”),然後和cJSON.c一起編譯即可使用。 

3,創建一個鍵值對

         首先是一個簡單的鍵值對字符串,要生成的目標如下:

{"firstName":"Brett"}

要進行創建,就是先確定鍵與值,然後轉爲cJSON格式。我們很容易就能明確鍵爲firstName,值爲Brett,可是,使用cJSON怎麼創建呢? 

對於這個簡單的例子,我們需要調用cJSON的五個接口函數就可以實現創建了。(有人不樂意了:都五個函數了,你還說“就可以了”----其實是一法通,百法通,學會了這個創建,其他的創建動作都是非常類似的。)

這五個函數的原型如下:

cJSON*cJSON_CreateObject ();

cJSON*cJSON_CreateString(const char *string);

voidcJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);

voidcJSON_Delete(cJSON *c);

char*cJSON_Print(cJSON *item); 

下面按創建過程來描述一次:

(1)       首先調用cJSON_ CreateObject ()函數,創建一個JSON對象,之後便可向這個對象中添加string或int等內容的數據項了。使用該函數會通過malloc()函數在內存中開闢一個空間,使用完成需要手動釋放。

        cJSON*root=cJSON_CreateObject();

(2)       調用cJSON_CreateString ()函數,由一個字符串生成一個cJSON的數據項。

        cJSON*item=cJSON_CreateString("Brett");

(3)       將上一步生成的數據項與其鍵值("firstName")一起添加到root對象中。

        cJSON_AddItemToObject(root,"firstName",item);

其實到這一步,我們在內存中的cJSON對象就已經構建完成了,後面是展示結果了。

(4)       將cJSON對象的內容解析爲字符串,並展示出來。

        out=cJSON_Print(root);

        printf("out:%s\n",out);

(5)       通過cJSON_Delete(),釋放cJSON_CreateObject ()分配出來的內存空間。

        cJSON_Delete(root);

(6)       釋放cJSON_Print ()分配出來的內存空間。

        free(out); 

         這樣就完成了一次cJSON接口調用,實現了字符串的創建工作。

4,轉換一個結構體 

接下來,我們來個複雜一點的,將一個結構體轉換爲JSON字符串,結構體定義如下:

typedefstruct

{

         char firstName[32];

         char lastName[32];

         char email[64];

         int age;

         float height;

} PERSON;

看起來比一個鍵值對複雜多了,我們又需要學習新的接口函數了嗎?

是的,由於出現了數字,我們需要學習一個新函數:

cJSON *cJSON_CreateNumber(double num);

當然,創建的步驟要更復雜一些,下面我仍然是按創建過程來描述一次: 

(1)還是先調用cJSON_ CreateObject ()函數,創建一個JSON對象root,做爲根(咱們可以把JSON串看成是一顆樹)。使用該函數會通過malloc()函數在內存中開闢一個空間,使用完成需要手動釋放。

        cJSON*root=cJSON_CreateObject();

(2)繼續調用cJSON_ CreateObject ()函數,創建一個JSON對象obj_person,做爲掛載結構體內容的對象。掛載內容之後,這個對象是要掛載到根上的。

        cJSON*obj_person=cJSON_CreateObject();

(3)根據數據生成cJSON格式的數據項,調用cJSON_AddItemToObject()函數掛載到obj_person對象上。這個過程,要多次重複,直到將所有數據都添加上。此時要注意,不同的成員,生成的方法是不一樣的。

        cJSON*item=cJSON_CreateString(person->firstName);

        cJSON_AddItemToObject(obj_person,"firstName",item);

        item=cJSON_CreateString(person->lastName);

        cJSON_AddItemToObject(obj_person,"lastName",item);

        item=cJSON_CreateString(person->email);

        cJSON_AddItemToObject(obj_person,"email",item);

        item=cJSON_CreateNumber(person->age);

        cJSON_AddItemToObject(obj_person,"age",item);

        item=cJSON_CreateNumber(person->height);

        cJSON_AddItemToObject(obj_person,"height",item);

(4)將obj_person對象掛載到根上。

        cJSON_AddItemToObject(root,"person",obj_person);

到這一步,我們在內存中的cJSON對象就已經構建完成了,後面就是展示結果。

(5)將cJSON對象的內容解析爲字符串,並展示出來。

        out=cJSON_Print(root);

        printf("out:%s\n",out);

(6)通過cJSON_Delete(),釋放cJSON_CreateObject ()分配出來的內存空間。這裏說明一下,我們前面調用了2次cJSON_CreateObject (),最後只需要針對root調用一次釋放即可,因爲第二次創建的對象也是掛接在root上的。

        cJSON_Delete(root);

(7)釋放cJSON_Print ()分配出來的內存空間。

        free(out); 

         至此,我們就使用cJSON接口完成了由結構體生成JSON字符串的工作。 

5,創建結構體數組的JSON串   

         最後,我們來個更復雜一些的,來轉換一個數組,並且數組的成員是結構體!我們要生成的目標如下:

{

"people":[

{"firstName":"z","lastName":"Jason","email":"[email protected]","height":1.67},

{"lastName":"jadena","email":"[email protected]","age":8,"height":1.17},

{"email":"[email protected]","firstName":"z","lastName":"Juliet","age":36,"height":1.55}

]

此時,我們又需要學習新的接口了,一個是創建數組,一個是取數組成員,函數原型如下:

cJSON*cJSON_CreateArray(void);

void   cJSON_AddItemToArray(cJSON *array, cJSON*item); 

由於前面已經實現了單個結構體的轉換,這裏我們重點關注下數組的相關調用。 

(1)還是先調用cJSON_ CreateObject ()函數,創建一個JSON對象root,做爲根。

(2)調用cJSON_CreateArray ()函數,創建一個JSON數組對象,準備掛載多個結構體對象。掛載內容之後,這個數組對象是要掛載到根上的。

        cJSON*array_person=cJSON_CreateArray();

(3)生成一個結構體對象,並相應添加數據,然後調用cJSON_AddItemToArray()函數掛載到數組對象上。這個過程,要多次重複,直到將所有結構體對象都添加上。

        cJSON_AddItemToArray(array_person,obj_person);

(4)將數組對象掛載到根上。

        cJSON_AddItemToObject(root,"people",array_person);

到這一步,我們在內存中的cJSON對象就已經構建完成了。

(5)將cJSON對象的內容解析爲字符串,並展示出來。

(6)通過cJSON_Delete(),釋放cJSON_CreateObject ()分配出來的內存空間。

(7)釋放cJSON_Print ()分配出來的內存空間。

         這樣,我們就使用cJSON接口完成了將結構體數組轉換成JSON字符串的工作。

詳細代碼見後文附帶例程。         

說明:

本文所附帶例程,實現了結構體數組生成JSON字符串,只是一個學習之作,對於初學cJSON的同學,可以有些借鑑參考的作用。 

附帶例程: 

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <sys/types.h>  
  4. #include <stdlib.h>  
  5. #include <unistd.h>  
  6.   
  7. #include "cJSON.h"  
  8.   
  9. typedef struct  
  10. {  
  11.     int id;  
  12.     char firstName[32];  
  13.     char lastName[32];  
  14.     char email[64];  
  15.     int age;  
  16.     float height;  
  17. }people;  
  18.   
  19. void dofile(char *filename);/* Read a file, parse, render back, etc. */  
  20.   
  21. int main(int argc, char **argv)  
  22. {  
  23.   
  24.     dofile("json_str4.txt");  
  25.   
  26.     return 0;  
  27. }  
  28.   
  29.   
  30. //create a key-value pair  
  31. int str_to_cJSON(char *json_string, char *str_val)  
  32. {  
  33.     char * out=NULL;  
  34.     cJSON *root=cJSON_CreateObject();  
  35.     if (!root)  
  36.     {  
  37.         printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  38.         return -1;  
  39.     }  
  40.     else  
  41.     {  
  42.         cJSON *item=cJSON_CreateString("Brett");  
  43.         cJSON_AddItemToObject(root,"firstName",item);  
  44.   
  45.         out=cJSON_Print(root);  
  46.         printf("out2:%s\n",out);  
  47.   
  48.         cJSON_Delete(root);  
  49.         if(out!=NULL)  
  50.         {  
  51.             free(out);  
  52.         }  
  53.     }  
  54.     return 0;  
  55. }  
  56.   
  57. //create a object from struct  
  58. int struct_to_cJSON(char *json_string, people *person)  
  59. {  
  60.   
  61.     if((json_string==NULL) || (person==NULL))  
  62.     {  
  63.         printf("%s: input is invalid",__func__);  
  64.     }  
  65.   
  66.     char * out=NULL;  
  67.     cJSON *root=cJSON_CreateObject();  
  68.   
  69.     if (!root)  
  70.     {  
  71.         printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  72.         return -1;  
  73.     }  
  74.     else  
  75.     {  
  76.         cJSON *obj_person=cJSON_CreateObject();  
  77.   
  78.         cJSON *item=cJSON_CreateString(person->firstName);  
  79.         cJSON_AddItemToObject(obj_person,"firstName",item);  
  80.   
  81.         item=cJSON_CreateString(person->lastName);  
  82.         cJSON_AddItemToObject(obj_person,"lastName",item);  
  83.   
  84.         item=cJSON_CreateString(person->email);  
  85.         cJSON_AddItemToObject(obj_person,"email",item);  
  86.   
  87.         item=cJSON_CreateNumber(person->age);  
  88.         cJSON_AddItemToObject(obj_person,"age",item);  
  89.   
  90.         item=cJSON_CreateNumber(person->height);  
  91.         cJSON_AddItemToObject(obj_person,"height",item);  
  92.   
  93.         cJSON_AddItemToObject(root,"person",obj_person);  
  94.   
  95.         out=cJSON_Print(root);  
  96.         printf("out2:%s\n",out);  
  97.   
  98.         cJSON_Delete(root);  
  99.         if(out!=NULL)  
  100.         {  
  101.             memcpy(json_string,out,strlen(out));  
  102.             free(out);  
  103.         }  
  104.     }  
  105.   
  106.     return 0;  
  107. }  
  108.   
  109.   
  110. //a struct array to CJSON  
  111. int struct_array_to_cJSON(char *text, people worker[])  
  112. {  
  113.     cJSON *json,*arrayItem,*item,*object;  
  114.     int i;  
  115.   
  116.     for(i=0;i<3;i++)  
  117.     {  
  118.         printf("i=%d, firstName=%s,lastName=%s,email=%s,age=%d,height=%f\n",  
  119.                 i,  
  120.                 worker[i].firstName,  
  121.                 worker[i].lastName,  
  122.                 worker[i].email,  
  123.                 worker[i].age,  
  124.                 worker[i].height);  
  125.     }  
  126.   
  127.     if((text==NULL) || (worker==NULL))  
  128.     {  
  129.         printf("%s: input is invalid",__func__);  
  130.     }  
  131.   
  132.     char * out=NULL;  
  133.     cJSON *root=cJSON_CreateObject();  
  134.   
  135.     if (!root)  
  136.     {  
  137.         printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  138.         return -1;  
  139.     }  
  140.     else  
  141.     {  
  142.         cJSON *array_person=cJSON_CreateArray();  
  143.   
  144.         for(i=0;i<3;i++)  
  145.         {  
  146.             cJSON *obj_person=cJSON_CreateObject();  
  147.   
  148.             cJSON *item=cJSON_CreateString(worker[i].firstName);  
  149.             cJSON_AddItemToObject(obj_person,"firstName",item);  
  150.   
  151.             item=cJSON_CreateString(worker[i].lastName);  
  152.             cJSON_AddItemToObject(obj_person,"lastName",item);  
  153.   
  154.             item=cJSON_CreateString(worker[i].email);  
  155.             cJSON_AddItemToObject(obj_person,"email",item);  
  156.   
  157.             item=cJSON_CreateNumber(worker[i].age);  
  158.             cJSON_AddItemToObject(obj_person,"age",item);  
  159.   
  160.             item=cJSON_CreateNumber(worker[i].height);  
  161.             cJSON_AddItemToObject(obj_person,"height",item);  
  162.   
  163.             cJSON_AddItemToArray(array_person,obj_person);  
  164.         }  
  165.   
  166.         cJSON_AddItemToObject(root,"people",array_person);  
  167.   
  168.         out=cJSON_Print(root);  
  169.         printf("out:%s\n",out);  
  170.   
  171.         cJSON_Delete(root);  
  172.         if(out!=NULL)  
  173.         {  
  174.             memcpy(text,out,strlen(out));  
  175.             free(out);  
  176.         }  
  177.     }  
  178.   
  179.     return 0;  
  180. }  
  181.   
  182. // create CJSON, write file  
  183. void dofile(char *filename)  
  184. {  
  185.     FILE *f;  
  186.     int len;  
  187.     char data[1024];  
  188.       
  189.     f=fopen(filename,"wb");  
  190.     fseek(f,0,SEEK_END);  
  191.     len=ftell(f);  
  192.     fseek(f,0,SEEK_SET);  
  193.       
  194.     printf("read file %s complete, len=%d.\n",filename,len);  
  195.   
  196. //  char str_name[40];  
  197. //  int ret = str_to_cJSON(data, str_name);  
  198.   
  199.     people worker[3]={  
  200.             {0,"zhong","Jason","[email protected]",0,1.67},  
  201.             {1,"","jadena","[email protected]",8,1.17},  
  202.             {2,"zhu","Juliet","[email protected]",36,1.55}  
  203.     };  
  204. //  struct_to_cJSON(data, &worker[1]);  
  205.     struct_array_to_cJSON(data, worker);  
  206.   
  207.     fwrite(data,1,strlen(data),f);  
  208.     fclose(f);  
  209.   
  210. }  



3.3.使用cJSON解析JSON字符串

          還是在Linux下,使用C語言編程,先實現讀文件的功能,然後開始JSON字符串的解析。我們還是一步步來,先從簡單的開始,萬丈高樓起於平地嘛。  

1,下載源碼;

可以從如下網站來下載:https://sourceforge.net/projects/cjson/ 。

2,包含cJSON的源碼;

         下載下來,解壓後,從裏面找到兩個文件(cJSON.c、cJSON.h),複製到我們的工程裏面。只需在函數中包含頭文件(#include “cJSON.h”),然後和cJSON.c一起編譯即可使用。 

3,解析一個鍵值對;  

         首先是一個簡單的鍵值對字符串,要解析的目標如下:

{"firstName":"Brett"}

要進行解析,也就是要分別獲取到鍵與值的內容。我們很容易就能看出鍵爲firstName,值爲Brett,可是,使用cJSON怎麼解析呢? 

         對於這個簡單的例子,只需要調用cJSON的三個接口函數就可以實現解析了,這三個函數的原型如下:

cJSON*cJSON_Parse(const char *value);

cJSON*cJSON_GetObjectItem(cJSON *object,const char *string);

voidcJSON_Delete(cJSON *c); 

下面按解析過程來描述一次:

(1)       首先調用cJSON_Parse()函數,解析JSON數據包,並按照cJSON結構體的結構序列化整個數據包。使用該函數會通過malloc()函數在內存中開闢一個空間,使用完成需要手動釋放。

cJSON*root=cJSON_Parse(json_string); 

(2)       調用cJSON_GetObjectItem()函數,可從cJSON結構體中查找某個子節點名稱(鍵名稱),如果查找成功可把該子節點序列化到cJSON結構體中。

cJSON*item=cJSON_GetObjectItem(root,"firstName"); 

(3)       如果需要使用cJSON結構體中的內容,可通過cJSON結構體中的valueint和valuestring取出有價值的內容(即鍵的值)

本例子中,我們直接訪問 item->valuestring 就獲取到 "Brett" 的內容了。

(4)       通過cJSON_Delete(),釋放cJSON_Parse()分配出來的內存空間。

cJSON_Delete(root);

         這樣就完成了一次cJSON接口調用,實現瞭解析工作。使用起來其實也很簡單的啊,呵呵。

4,解析一個結構體;        

         接下來,我們來個複雜一點的,解析一個結構體,要解析的目標如下:

{

         "person":

         {

                   "firstName":"z",

                   "lastName":"jadena",

                   "email":"[email protected]",

                   "age":8,

                   "height":1.17

         }       

}

看起來比一個鍵值對複雜多了,我們又需要學習新的接口函數了嗎?

         答案是不需要!

         還是那三個函數就可以了。當然,解析的步驟要複雜一些了,下面我按解析過程來描述一次: 

(1)根據JSON串中的對象,我們定義一個相應的結構體如下:

typedefstruct

{

         char firstName[32];

         char lastName[32];

         char email[64];

         int age;

         float height;

} PERSON;

具體的對應關係,一目瞭然,我就不羅嗦了。讓我們直奔主題,解析!     

(2)還是調用cJSON_Parse()函數,解析JSON數據包。

cJSON*root=cJSON_Parse(json_string); 

(3)調用一次cJSON_GetObjectItem()函數,獲取到對象person。

cJSON *object=cJSON_GetObjectItem(root,"person"); 

(4)對我們剛取出來的對象person,多次調用cJSON_GetObjectItem()函數,來獲取對象的成員。此時要注意,不同的成員,訪問的方法不一樣:

cJSON*item;

PERSONperson;

item=cJSON_GetObjectItem(object,"firstName");

memcpy(person.firstName,item->valuestring,strlen(item->valuestring));

item=cJSON_GetObjectItem(object,"lastName");

memcpy(person.lastName,item->valuestring,strlen(item->valuestring));

item=cJSON_GetObjectItem(object,"email");

memcpy(person.email,item->valuestring,strlen(item->valuestring));

item=cJSON_GetObjectItem(object,"age");

person.age=item->valueint;

item=cJSON_GetObjectItem(object,"height");

person.height=item->valuedouble;

這樣,就獲取到了對象的全部內容了。

(5)       通過cJSON_Delete(),釋放cJSON_Parse()分配出來的內存空間。

cJSON_Delete(root);

         至此,我們就使用cJSON接口完成了基於結構體的解析工作。 

5,解析結構體數組的JSON串;          

         最後,我們來個更復雜一些的,來解析一個數組,並且數組的成員是結構體!要解析的JSON串如下:

{

"people":[

{"firstName":"z","lastName":"Jason","email":"[email protected]","height":1.67},

{"lastName":"jadena","email":"[email protected]","age":8,"height":1.17},

{"email":"[email protected]","firstName":"z","lastName":"Juliet","age":36,"height":1.55}

]

         此時,我們真的又需要學習新的接口了,一個是獲取數組長度,一個是取數組成員,函數原型如下:

int    cJSON_GetArraySize(cJSON *array);

cJSON*cJSON_GetArrayItem(cJSON *array,int item); 

         由於前面已經實現了結構體的解析,這裏我們只需要關注下數組的相關調用即可。 

(1)調用cJSON_Parse()函數,解析JSON數據包。

(2)調用一次cJSON_GetObjectItem()函數,獲取到數組people。

(3)對我們剛取出來的數組people,調用cJSON_GetArraySize()函數,來獲取數組中對象的個數。然後,多次調用cJSON_GetArrayItem()函數,逐個讀取數組中對象的內容。

(4)通過cJSON_Delete(),釋放cJSON_Parse()分配出來的內存空間。

         這樣,我們就使用cJSON接口完成了結構體數組的解析工作。

詳細代碼見後文附帶例程。        

說明:

本文所附帶例程,實現了結構體數組的解析,只是一個學習之作,對於初學JSON使用cJSON接口的同學,可以有些借鑑參考的作用。 

附帶例程: 

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <sys/types.h>  
  4. #include <stdlib.h>  
  5. #include <unistd.h>  
  6.   
  7. #include "cJSON.h"  
  8.   
  9. typedef struct  
  10. {  
  11.     int id;  
  12.     char firstName[32];  
  13.     char lastName[32];  
  14.     char email[64];  
  15.     int age;  
  16.     float height;  
  17. }people;  
  18.   
  19. void dofile(char *filename);/* Read a file, parse, render back, etc. */  
  20.   
  21. int main(int argc, char **argv)  
  22. {  
  23.   
  24. //  dofile("json_str1.txt");  
  25. //  dofile("json_str2.txt");  
  26.     dofile("json_str3.txt");  
  27.   
  28.     return 0;  
  29. }  
  30.   
  31. //parse a key-value pair  
  32. int cJSON_to_str(char *json_string, char *str_val)  
  33. {  
  34.     cJSON *root=cJSON_Parse(json_string);  
  35.     if (!root)  
  36.     {  
  37.         printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  38.         return -1;  
  39.     }  
  40.     else  
  41.     {  
  42.         cJSON *item=cJSON_GetObjectItem(root,"firstName");  
  43.         if(item!=NULL)  
  44.         {  
  45.             printf("cJSON_GetObjectItem: type=%d, key is %s, value is %s\n",item->type,item->string,item->valuestring);  
  46.             memcpy(str_val,item->valuestring,strlen(item->valuestring));  
  47.         }  
  48.         cJSON_Delete(root);  
  49.     }  
  50.     return 0;  
  51. }  
  52.   
  53. //parse a object to struct  
  54. int cJSON_to_struct(char *json_string, people *person)  
  55. {  
  56.     cJSON *item;  
  57.     cJSON *root=cJSON_Parse(json_string);  
  58.     if (!root)  
  59.     {  
  60.         printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  61.         return -1;  
  62.     }  
  63.     else  
  64.     {  
  65.         cJSON *object=cJSON_GetObjectItem(root,"person");  
  66.         if(object==NULL)  
  67.         {  
  68.             printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  69.             cJSON_Delete(root);  
  70.             return -1;  
  71.         }  
  72.         printf("cJSON_GetObjectItem: type=%d, key is %s, value is %s\n",object->type,object->string,object->valuestring);  
  73.   
  74.         if(object!=NULL)  
  75.         {  
  76.             item=cJSON_GetObjectItem(object,"firstName");  
  77.             if(item!=NULL)  
  78.             {  
  79.                 printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);  
  80.                 memcpy(person->firstName,item->valuestring,strlen(item->valuestring));  
  81.             }  
  82.   
  83.             item=cJSON_GetObjectItem(object,"lastName");  
  84.             if(item!=NULL)  
  85.             {  
  86.                 printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);  
  87.                 memcpy(person->lastName,item->valuestring,strlen(item->valuestring));  
  88.             }  
  89.   
  90.             item=cJSON_GetObjectItem(object,"email");  
  91.             if(item!=NULL)  
  92.             {  
  93.                 printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);  
  94.                 memcpy(person->email,item->valuestring,strlen(item->valuestring));  
  95.             }  
  96.   
  97.             item=cJSON_GetObjectItem(object,"age");  
  98.             if(item!=NULL)  
  99.             {  
  100.                 printf("cJSON_GetObjectItem: type=%d, string is %s, valueint=%d\n",item->type,item->string,item->valueint);  
  101.                 person->age=item->valueint;  
  102.             }  
  103.             else  
  104.             {  
  105.                 printf("cJSON_GetObjectItem: get age failed\n");  
  106.             }  
  107.   
  108.             item=cJSON_GetObjectItem(object,"height");  
  109.             if(item!=NULL)  
  110.             {  
  111.                 printf("cJSON_GetObjectItem: type=%d, string is %s, valuedouble=%f\n",item->type,item->string,item->valuedouble);  
  112.                 person->height=item->valuedouble;  
  113.             }  
  114.         }  
  115.   
  116.         cJSON_Delete(root);  
  117.     }  
  118.     return 0;  
  119. }  
  120.   
  121. //parse a struct array  
  122. int cJSON_to_struct_array(char *text, people worker[])  
  123. {  
  124.     cJSON *json,*arrayItem,*item,*object;  
  125.     int i;  
  126.   
  127.     json=cJSON_Parse(text);  
  128.     if (!json)  
  129.     {  
  130.         printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  131.     }  
  132.     else  
  133.     {  
  134.         arrayItem=cJSON_GetObjectItem(json,"people");  
  135.         if(arrayItem!=NULL)  
  136.         {  
  137.             int size=cJSON_GetArraySize(arrayItem);  
  138.             printf("cJSON_GetArraySize: size=%d\n",size);  
  139.   
  140.             for(i=0;i<size;i++)  
  141.             {  
  142.                 printf("i=%d\n",i);  
  143.                 object=cJSON_GetArrayItem(arrayItem,i);  
  144.   
  145.                 item=cJSON_GetObjectItem(object,"firstName");  
  146.                 if(item!=NULL)  
  147.                 {  
  148.                     printf("cJSON_GetObjectItem: type=%d, string is %s\n",item->type,item->string);  
  149.                     memcpy(worker[i].firstName,item->valuestring,strlen(item->valuestring));  
  150.                 }  
  151.   
  152.                 item=cJSON_GetObjectItem(object,"lastName");  
  153.                 if(item!=NULL)  
  154.                 {  
  155.                     printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);  
  156.                     memcpy(worker[i].lastName,item->valuestring,strlen(item->valuestring));  
  157.                 }  
  158.   
  159.                 item=cJSON_GetObjectItem(object,"email");  
  160.                 if(item!=NULL)  
  161.                 {  
  162.                     printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);  
  163.                     memcpy(worker[i].email,item->valuestring,strlen(item->valuestring));  
  164.                 }  
  165.   
  166.                 item=cJSON_GetObjectItem(object,"age");  
  167.                 if(item!=NULL)  
  168.                 {  
  169.                     printf("cJSON_GetObjectItem: type=%d, string is %s, valueint=%d\n",item->type,item->string,item->valueint);  
  170.                     worker[i].age=item->valueint;  
  171.                 }  
  172.                 else  
  173.                 {  
  174.                     printf("cJSON_GetObjectItem: get age failed\n");  
  175.                 }  
  176.   
  177.                 item=cJSON_GetObjectItem(object,"height");  
  178.                 if(item!=NULL)  
  179.                 {  
  180.                     printf("cJSON_GetObjectItem: type=%d, string is %s, value=%f\n",item->type,item->string,item->valuedouble);  
  181.                     worker[i].height=item->valuedouble;  
  182.                 }  
  183.             }  
  184.         }  
  185.   
  186.         for(i=0;i<3;i++)  
  187.         {  
  188.             printf("i=%d, firstName=%s,lastName=%s,email=%s,age=%d,height=%f\n",  
  189.                     i,  
  190.                     worker[i].firstName,  
  191.                     worker[i].lastName,  
  192.                     worker[i].email,  
  193.                     worker[i].age,  
  194.                     worker[i].height);  
  195.         }  
  196.   
  197.         cJSON_Delete(json);  
  198.     }  
  199.     return 0;  
  200. }  
  201.   
  202. // Read a file, parse, render back, etc.  
  203. void dofile(char *filename)  
  204. {  
  205.     FILE *f;  
  206.     int len;  
  207.     char *data;  
  208.       
  209.     f=fopen(filename,"rb");  
  210.     fseek(f,0,SEEK_END);  
  211.     len=ftell(f);  
  212.     fseek(f,0,SEEK_SET);  
  213.     data=(char*)malloc(len+1);  
  214.     fread(data,1,len,f);  
  215.     fclose(f);  
  216.       
  217.     printf("read file %s complete, len=%d.\n",filename,len);  
  218.   
  219. //  char str_name[40];  
  220. //  int ret = cJSON_to_str(data, str_name);  
  221.   
  222. //  people person;  
  223. //  int ret = cJSON_to_struct(data, &person);  
  224.   
  225.     people worker[3]={{0}};  
  226.     cJSON_to_struct_array(data, worker);  
  227.   
  228.     free(data);  
  229. }  


到此,讀者可基本掌握cjson的使用,如果僅僅爲使用,基本可滿足需求。想進一步學習,可繼續閱讀。

4.cjson源碼解析

4.1.cjson基本數據結構

1)文本數據 
cjson處理的數據爲json語言寫的文本文件。
 2)json類型定義
json 的 value 存在這麼幾種類型: object對象, array數組, string字符串, number數字, true, false, null。
其中對象是一個 key-value 的集合, 而數組是一些 value 的有序列表。
cjson 中在 頭文件中定義了 這些類型的數字編號和 cJSON value 的結構體。
 
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
有網友建議用emnu 來實現類型的定義,如:
 
enum {cJSON_False, cJSON_True, cJSON_NULL, cJSON_Number, cJSON_String, cJSON_Array, cJSON_Object, cJSON_IsReference=256};
3)cJSON結構體
 
/* The cJSON structure: */
typedef struct cJSON {
    struct cJSON *next,*prev;   /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
    struct cJSON *child;        /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
    int type;                   /* The type of the item, as above. */
    char *valuestring;          /* The item's string, if type==cJSON_String */
    int valueint;               /* The item's number, if type==cJSON_Number */
    double valuedouble;         /* The item's number, if type==cJSON_Number */
    char *string;               /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
該結構體是cJSON處理數據的主要數據結構,也是其構建鏈表結構的重要組成元素。

4.2.節點操作

4.2.1.json 內存管理--hook 管理函數

在 c 語言中內存一般是 malloc 和 free 的。
爲了方便用戶自由的管理內存, cjson 使用 Hook 技術來讓使用者可以自定義內存管理函數。
即用戶自定義 malloc 和 free.
具體實現方式可以參考下面的代碼, 默認使用系統的 malloc 和 free 函數, 用過 cJSON_InitHooks 函數可以替換成用戶自定義的 malloc 和 free 函數。
 
typedef struct cJSON_Hooks {
    void *(*malloc_fn)(size_t sz);
    void (*free_fn)(void *ptr);
} cJSON_Hooks;
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
void cJSON_InitHooks(cJSON_Hooks* hooks) {
    if (!hooks) { /* Reset hooks */
        cJSON_malloc = malloc;
        cJSON_free = free;
        return;
    }
    cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
    cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
}
注:
利用函數指針,定義自己的處理函數,並且可應用系統函數。

4.2.2.節點操作

在內存管理基礎上,可以創建不同類型的節點,這些節點保存的就是json的value值,即key-value鍵值對。

通過節點操作可以創建的節點連接起來,組成一棵樹。

一個json文件可以理解爲一棵樹形數據結構。

數據存儲就是鏈表操作,就回到了常見數據結構的操作。

這裏指的就是節點操作,將節點 a 添加爲節點 b 的兒子,將節點 b 從節點 a 的兒子中刪除,或者修改節點 a 的值或者查詢節點 a 的值等,變爲鏈表基本操作: 增刪改查 

4.2.3.創建節點

1)基本操作:
 
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
    cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
    if (node) memset(node,0,sizeof(cJSON));
    return node;
}
後面操作均在此基礎上展開。
2)創建節點操作
 
/* Create basic types: */
cJSON *cJSON_CreateNull(void)                   {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void)                   {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void)                  {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b)                  {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
cJSON *cJSON_CreateNumber(double num)           {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
cJSON *cJSON_CreateString(const char *string)   {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void)                  {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void)                 {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(const int *numbers,int count)       {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(const float *numbers,int count)   {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count)  {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateObject(void)爲常見操作,
3)附帶一個複製字符串操作
 
static char* cJSON_strdup(const char* str)
{
      size_t len;
      char* copy;
      len = strlen(str) + 1;
      if (!(copy = (char*)cJSON_malloc(len))) return 0;
      memcpy(copy,str,len);
      return copy;
}

4.2.4.刪除節點

刪除節點很簡單, 先刪除兒子,然後清理內存即可。

總結一下就是對於 object 和 array 需要先刪除兒子,然後刪除自己。
對於 字符串, 需要先釋放字符串的內存, 再釋放自己這塊內存。
對於其他節點,直接釋放自己這塊內存。

 
/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{
    cJSON *next;
    while (c)
    {
        next=c->next;
        if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
        if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
        if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string);
        cJSON_free(c);
        c=next;
    }
}

4.2.5.添加兒子節點

添加兒子節點有兩種情況,一種是給 object 增加兒子, 一種是給 array 增加兒子。

object 和 array 相比, 僅僅多了一個操作 ,即設置 key .

所以我們可以再 object 中設置完 key 之後再調用 給 array 添加兒子的操作來實現給 object 添加兒子。

具體參考膽碼。

 
/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {
    //兩個兄弟的指針互相指向對方
    prev->next=item; 
    item->prev=prev;
}
/* Add item to array/object. */
void   cJSON_AddItemToArray(cJSON *array, cJSON *item) {
    cJSON *c=array->child;
    if (!item) return;
    if (!c) {
        array->child=item; //之前沒有兒子,直接添加
    } else {
        while (c && c->next) c=c->next; // 先找到最後一個兒子。
        suffix_object(c,item); // 添加兒子, c 是 item 的兄弟
    }
}
void   cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {
    if (!item) return;
    if (item->string) cJSON_free(item->string);//這個 兒子之前有key, 先清理了。  
    item->string=cJSON_strdup(string); // 設置 key
    cJSON_AddItemToArray(object,item); // 添加兒子
}
實際上上面這兩個操作即可滿足我們的添加需求了。

但是 cjson 爲了我們更方便的使用添加節點的操作, 它又封裝了一些操作, 當然使用宏定義封裝的。

比如我們平常給 object 增加一個 false 兒子需要這樣

cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
現在我們只需要這樣

cJSON_AddFalseToObject(object,name)
具體實現方式就是定義一個宏。

而且 cjson 只定義了對象的添加,而沒有對數組定義這個宏。

大概原因是那時候, 一般一個數組內的元素的類型都是相同的吧, 不像對象這麼靈活。

/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name)      cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name)      cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name)    cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b)    cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n)  cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s)  cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
因此 cjson 還專門爲 數組定義了下面的批量創建節點。

/* These utilities create an Array of count items. */
cJSON *cJSON_CreateIntArray(const int *numbers,int count);
cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
cJSON *cJSON_CreateStringArray(const char **strings,int count);
另外, 當我們要添加的節點已經在一個樹上的時候, 再向另一個樹中添加這個節點時, 這個節點的 pre 和 next 指針會被覆蓋。

於是 cjson 又提供了一種引用性添加節點的方法。

簡單的說就是在創建一個 item, 新創建的 item 的 value 指針直接指向原來的 value 值, 這樣兩個 item 就指向了同一個 item 了。

但是這個引用計數是個難題, cjson 也沒有處理好, 只能引用一次, 大家可以想象怎麼解決。

我們先來看看 cjson 的引用是怎麼實現的。

/* Utility for handling references. */
static cJSON *create_reference(cJSON *item) {
    cJSON *ref=cJSON_New_Item();
    if (!ref) return 0;
    memcpy(ref,item,sizeof(cJSON));
    ref->string=0;
    ref->type|=cJSON_IsReference;
    ref->next=ref->prev=0;
    return ref;
}
上面的引用計數僅僅存在 type 裏面,顯示是有問題的。

我們的 value 是保持不變的,所有的引用都指向這個value.

所以我們可以通過一個和 value 類似的東西, 大家都指向這個 東西, 新增加一個引用的時候加1, 釋放一個引用的時候減一即可。

這個看着怎麼那麼像智能指針呢?

4.2.6.刪除兒子節點

刪除也是從 array 和 object 中刪除,實現就比較簡潔了。

void  cJSON_DeleteItemFromArray(cJSON *array,int which) {
    cJSON_Delete(cJSON_DetachItemFromArray(array,which));
}
void  cJSON_DeleteItemFromObject(cJSON *object,const char *string) {
    cJSON_Delete(cJSON_DetachItemFromObject(object,string));
}
Detach 是什麼東西呢?

我們把一個節點從 json 樹中刪除, 但是不釋放內存,而是先保留這個節點的指針, 這樣儲存在這個節點的信息都保留了下來。

接下來我們就可以做很多事了, 合適的時候添加到其他對象中, 合適的時候釋放內存。

比如上面的 delete 函數, 就需要真實的刪除了, 這個時候我們刪除即可。

而 detach 實現也比較簡單, 只是少了一步刪除操作。
 
// 節點從雙向鏈表中刪除即可
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {
    cJSON *c=array->child;
    while (c && which>0) c=c->next,which--;
    if (!c) return 0;
    if (c->prev) c->prev->next=c->next;
    if (c->next) c->next->prev=c->prev;
    if (c==array->child) array->child=c->next;
    c->prev=c->next=0;
    return c;
}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {
    int i=0;
    cJSON *c=object->child;
    while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;
    if (c) return cJSON_DetachItemFromArray(object,i);
    return 0;
}

4.2.7.查找節點

對於一般類型的item, 我們直接就得到對應的節點.

但是對於 array 和 object , 我們需要查找對應的節點, 所以就需要去查找了。

這個查找算法由 cjson 的儲存節點方式決定着。

由於cjson 採用鏈表儲存了, 所以查找當時只能是暴力遍歷了。

 
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {
    cJSON *c=array->child;
    while (c && item>0) item--,c=c->next;
    return c;
}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {
    cJSON *c=object->child;
    while (c && cJSON_strcasecmp(c->string,string)) c=c->next;
    return c;
}

4.2.8.修改節點

我們查找到對應的節點了,就可以對節點進行簡單的修改了。

什麼是簡單的修改呢?

節點的類型不是 array 和 object 都可以算是簡單類型,可以直接修改修改其值即可。

但是對於 array 和 object, 我們想給他賦值的話,涉及到釋放就得內存這個問題。

下面我們來看看 cjson 的實現代碼。

 
/* Replace array/object items with new ones. */
void   cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {
    cJSON *c=array->child;
    while (c && which>0) c=c->next,which--;
    if (!c) return;
    newitem->next=c->next;
    newitem->prev=c->prev;
    if (newitem->next) newitem->next->prev=newitem;
    if (c==array->child) array->child=newitem;
    else newitem->prev->next=newitem;
    c->next=c->prev=0;
    cJSON_Delete(c);
}
void   cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem) {
    int i=0;
    cJSON *c=object->child;
    while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;
    if(c) {
        newitem->string=cJSON_strdup(string);
        cJSON_ReplaceItemInArray(object,i,newitem);
    }
}


4.3.解析概述

4.3.1.解析總的調用操作

 
/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) {
    const char *end=0;
    cJSON *c=cJSON_New_Item();
    ep=0;
    if (!c) return 0;       /* memory fail */
    end=parse_value(c,skip(value));
    if (!end) {
        cJSON_Delete(c);    /* parse failure. ep is set. */
        return 0;
    }
    /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
    if (require_null_terminated) {
        end=skip(end);
        if (*end) {
            cJSON_Delete(c);
            ep=end;
            return 0;
        }
    }
    if (return_parse_end) *return_parse_end=end;
    return c;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {
    return cJSON_ParseWithOpts(value,0,0);
}
上面兩個函數, 其實對我們有用的只有一句 end=parse_value(c,skip(value));
, 也就是我們只需要瞭解一下 parse_value 函數即可。
下面是parse_value函數:
 
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value) {
    if (!value)return 0;/* Fail on null. */
    if (!strncmp(value,"null",4)) {
        item->type=cJSON_NULL;
        return value+4;
    }
    if (!strncmp(value,"false",5)) {
        item->type=cJSON_False;
        return value+5;
    }
    if (!strncmp(value,"true",4)) {
        item->type=cJSON_True;
        item->valueint=1;
        return value+4;
    }
    if (*value=='\"') {
        return parse_string(item,value);
    }
    if (*value=='-' || (*value>='0' && *value<='9')) {
        return parse_number(item,value);
    }
    if (*value=='[') {
        return parse_array(item,value);
    }
    if (*value=='{') {
        return parse_object(item,value);
    }
    ep=value;
    return 0;/* failure. */
}

parse_value 的實現方式很簡單, 根據前幾個字符來判斷寫一個類型是什麼。如果是 null, false 或 true 設置類型,並返回偏移指針。如果是其他的,則進入對應的函數中。

順帶提一下,用到的一個函數:
 
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {
    while (in && *in && (unsigned char)*in<=32) in++;
    return in;
}
skip 用於用於忽略空白,這裏跳過了 ascii 值小於 32 

4.3.2.解析字符串

解析字符串時, 對於特殊字符也應該轉義,比如 "\\n" 字符應該轉換爲 '\n' 這個換行符。當然,如果只有特殊字符轉換的話,代碼不會又這麼長, 對於字符串, 還要支持非 ascii 碼的字符, 即 utf8字符。這些字符在字符串中會編碼爲 \uXXXX 的字符串, 我們現在需要還原爲 0-255 的一個字符。

static unsigned parse_hex4(const char *str) {
    unsigned h=0;
    if (*str>='0' && *str<='9') h+=(*str)-'0';
    else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
    else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
    else return 0;
    h=h<<4;
    str++;
    if (*str>='0' && *str<='9') h+=(*str)-'0';
    else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
    else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
    else return 0;
    h=h<<4;
    str++;
    if (*str>='0' && *str<='9') h+=(*str)-'0';
    else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
    else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
    else return 0;
    h=h<<4;
    str++;
    if (*str>='0' && *str<='9') h+=(*str)-'0';
    else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
    else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
    else return 0;
    return h;
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str) {
    const char *ptr=str+1;
    char *ptr2;
    char *out;
    int len=0;
    unsigned uc,uc2;
    if (*str!='\"') {
        ep=str;    /* not a string! */
        return 0;
    }
    while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++;/* Skip escaped quotes. */
    out=(char*)cJSON_malloc(len+1);/* This is how long we need for the string, roughly. */
    if (!out) return 0;
    ptr=str+1;
    ptr2=out;
    while (*ptr!='\"' && *ptr) {
        if (*ptr!='\\') *ptr2++=*ptr++;
        else {
            ptr++;
            switch (*ptr) {
            case 'b':
                *ptr2++='\b';
                break;
            case 'f':
                *ptr2++='\f';
                break;
            case 'n':
                *ptr2++='\n';
                break;
            case 'r':
                *ptr2++='\r';
                break;
            case 't':
                *ptr2++='\t';
                break;
            case 'u': /* transcode utf16 to utf8. */
                uc=parse_hex4(ptr+1);
                ptr+=4;/* get the unicode char. */
                if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0)break;/* check for invalid.*/
                if (uc>=0xD800 && uc<=0xDBFF) {/* UTF16 surrogate pairs.*/
                    if (ptr[1]!='\\' || ptr[2]!='u')break;/* missing second-half of surrogate.*/
                    uc2=parse_hex4(ptr+3);
                    ptr+=6;
                    if (uc2<0xDC00 || uc2>0xDFFF)break;/* invalid second-half of surrogate.*/
                    uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
                }
                len=4;
                if (uc<0x80) len=1;
                else if (uc<0x800) len=2;
                else if (uc<0x10000) len=3;
                ptr2+=len;
                switch (len) {
                case 4:
                    *--ptr2 =((uc | 0x80) & 0xBF);
                    uc >>= 6;
                case 3:
                    *--ptr2 =((uc | 0x80) & 0xBF);
                    uc >>= 6;
                case 2:
                    *--ptr2 =((uc | 0x80) & 0xBF);
                    uc >>= 6;
                case 1:
                    *--ptr2 =(uc | firstByteMark[len]);
                }
                ptr2+=len;
                break;
            default:
                *ptr2++=*ptr;
                break;
            }
            ptr++;
        }
    }
    *ptr2=0;
    if (*ptr=='\"') ptr++;
    item->valuestring=out;
    item->type=cJSON_String;
    return ptr;
}
static unsigned parse_hex4(const char *str)用於解析16進制。

4.3.3.解析數字

數字解析需要考慮科學計數法。
 
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num) {
    double n=0,sign=1,scale=0;
    int subscale=0,signsubscale=1;
    if (*num=='-') sign=-1,num++;/* Has sign? */
    if (*num=='0') num++;/* is zero */
    if (*num>='1' && *num<='9')don=(n*10.0)+(*num++ -'0');
    while (*num>='0' && *num<='9');/* Number? */
    if (*num=='.' && num[1]>='0' && num[1]<='9') {
        num++;   /* Fractional part? */
        don=(n*10.0)+(*num++ -'0'),scale--;
        while (*num>='0' && *num<='9');
    }
    if (*num=='e' || *num=='E') {/* Exponent? */
        num++;
        if (*num=='+') num++;
        else if (*num=='-') signsubscale=-1,num++;/* With sign? */
        while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');/* Number? */
    }
    n=sign*n*pow(10.0,(scale+subscale*signsubscale));/* number = +/- number.fraction * 10^+/- exponent */
    item->valuedouble=n;
    item->valueint=(int)n;
    item->type=cJSON_Number;
    return num;
}

4.3.4.解析數組

解析數組, 需要先遇到 '[' 這個符號, 然後挨個的讀取節點內容, 節點使用 ',' 分隔, ',' 前後還可能有空格, 最後以 ']' 結尾。我們要編寫的也是這樣。先創建一個數組對象, 判斷是否有兒子, 有的話讀取第一個兒子, 然後判斷是不是有 逗號, 有的話循環讀取後面的兒子。最後讀取 ']' 即可。
 
/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value) {
    cJSON *child;
    if (*value!='[') {
        ep=value;    /* not an array! */
        return 0;
    }
    item->type=cJSON_Array;
    value=skip(value+1);
    if (*value==']') return value+1;/* empty array. */
    item->child=child=cJSON_New_Item();
    if (!item->child) return 0; /* memory fail */
    value=skip(parse_value(child,skip(value)));/* skip any spacing, get the value. */
    if (!value) return 0;
    while (*value==',') {
        cJSON *new_item;
        if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
        child->next=new_item;
        new_item->prev=child;
        child=new_item;
        value=skip(parse_value(child,skip(value+1)));
        if (!value) return 0;/* memory fail */
    }
    if (*value==']') return value+1;/* end of array */
    ep=value;
    return 0;/* malformed. */
}

4.3.5.解析對象

解析對象和解析數組類似, 只不過對象的一個兒子是個 key-value, key 是字符串, value 可能是任何值, key 和 value 用 ":" 分隔。
 
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value) {
    cJSON *child;
    if (*value!='{') {
        ep=value;    /* not an object! */
        return 0;
    }
    item->type=cJSON_Object;
    value=skip(value+1);
    if (*value=='}') return value+1;/* empty array. */
    item->child=child=cJSON_New_Item();
    if (!item->child) return 0;
    value=skip(parse_string(child,skip(value)));
    if (!value) return 0;
    child->string=child->valuestring;
    child->valuestring=0;
    if (*value!=':') {
        ep=value;    /* fail! */
        return 0;
    }
    value=skip(parse_value(child,skip(value+1)));/* skip any spacing, get the value. */
    if (!value) return 0;
    while (*value==',') {
        cJSON *new_item;
        if (!(new_item=cJSON_New_Item()))return 0; /* memory fail */
        child->next=new_item;
        new_item->prev=child;
        child=new_item;
        value=skip(parse_string(child,skip(value+1)));
        if (!value) return 0;
        child->string=child->valuestring;
        child->valuestring=0;
        if (*value!=':') {
            ep=value;    /* fail! */
            return 0;
        }
        value=skip(parse_value(child,skip(value+1)));/* skip any spacing, get the value. */
        if (!value) return 0;
    }
    if (*value=='}') return value+1;/* end of array */
    ep=value;
    return 0;/* malformed. */
}
注:各種解析之間有相互調用關係。

4.4.json序列化--打印輸出或保存

json 序列化也成爲把 json 輸出出來。

一般有兩種輸出:格式化輸出,壓縮輸出。

簡單的說就是要不要輸出一些空白的問題。

 
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {
    return print_value(item,0,1);
}
char *cJSON_PrintUnformatted(cJSON *item) {
    return print_value(item,0,0);
}
/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt) {
    char *out=0;
    if (!item) return 0;
    switch ((item->type)&255) {
    case cJSON_NULL:
        out=cJSON_strdup("null");
        break;
    case cJSON_False:
        out=cJSON_strdup("false");
        break;
    case cJSON_True:
        out=cJSON_strdup("true");
        break;
    case cJSON_Number:
        out=print_number(item);
        break;
    case cJSON_String:
        out=print_string(item);
        break;
    case cJSON_Array:
        out=print_array(item,depth,fmt);
        break;
    case cJSON_Object:
        out=print_object(item,depth,fmt);
        break;
    }
    return out;
}

由於基本類型輸出的實現比較簡單,這裏就不多說了,這裏只說說輸出 對象的實現吧。

假設我們要使用格式化輸出, 也就是美化輸出。

cjson 的做法不是邊分析 json 邊輸出, 而是預先將要輸的內容全部按字符串存在內存中, 最後輸出整個字符串。

這對於比較大的 json 來說, 內存就是個問題了。

另外,格式化輸出依靠的是節點的深度, 這個也可以優化, 一般寬度超過80 時, 就需要從新的一行算起的。

 
/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt) {
    char **entries=0,**names=0;
    char *out=0,*ptr,*ret,*str;
    int len=7,i=0,j;
    cJSON *child=item->child;
    int numentries=0,fail=0;
    /* Count the number of entries. */
    while (child) numentries++,child=child->next;
    /* Explicitly handle empty object case */
    if (!numentries) {
        out=(char*)cJSON_malloc(fmt?depth+4:3);
        if (!out)return 0;
        ptr=out;
        *ptr++='{';
        if (fmt) {
            *ptr++='\n';
            for (i=0; i<depth-1; i++) *ptr++='\t';
        }
        *ptr++='}';
        *ptr++=0;
        return out;
    }
    /* Allocate space for the names and the objects */
    entries=(char**)cJSON_malloc(numentries*sizeof(char*));
    if (!entries) return 0;
    names=(char**)cJSON_malloc(numentries*sizeof(char*));
    if (!names) {
        cJSON_free(entries);
        return 0;
    }
    memset(entries,0,sizeof(char*)*numentries);
    memset(names,0,sizeof(char*)*numentries);
    /* Collect all the results into our arrays: */
    child=item->child;
    depth++;
    if (fmt) len+=depth;
    while (child) {
        names[i]=str=print_string_ptr(child->string);
        entries[i++]=ret=print_value(child,depth,fmt);
        if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0);
        else fail=1;
        child=child->next;
    }
    /* Try to allocate the output string */
    if (!fail) out=(char*)cJSON_malloc(len);
    if (!out) fail=1;
    /* Handle failure */
    if (fail) {
        for (i=0; i<numentries; i++) {
            if (names[i]) cJSON_free(names[i]);
            if (entries[i]) cJSON_free(entries[i]);
        }
        cJSON_free(names);
        cJSON_free(entries);
        return 0;
    }
    /* Compose the output: */
    *out='{';
    ptr=out+1;
    if (fmt)*ptr++='\n';
    *ptr=0;
    for (i=0; i<numentries; i++) {
        if (fmt) for (j=0; j<depth; j++) *ptr++='\t';
        strcpy(ptr,names[i]);
        ptr+=strlen(names[i]);
        *ptr++=':';
        if (fmt) *ptr++='\t';
        strcpy(ptr,entries[i]);
        ptr+=strlen(entries[i]);
        if (i!=numentries-1) *ptr++=',';
        if (fmt) *ptr++='\n';
        *ptr=0;
        cJSON_free(names[i]);
        cJSON_free(entries[i]);
    }
    cJSON_free(names);
    cJSON_free(entries);
    if (fmt) for (i=0; i<depth-1; i++) *ptr++='\t';
    *ptr++='}';
    *ptr++=0;
    return out;
}

5.參考文章.














發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章