【轉】cJSON 使用筆記


 緣      起                                                                                                     

      最近在stm32f103上做一個智能家居的項目,其中選擇的實時操作系統是 rt_thread OS v1.2.2穩定版本,其中涉及到C和java(android)端數據的交換問題,經過討論和研究,選擇了json格式的數據進行交互。當然,如果自己去寫一個json解析器,有點重造輪子的嫌疑。於是使用了開源的json解析器。考慮到是嵌入式平台,在一位朋友的推薦下,選擇了輕量級別的cJSON。

 使      用                                                                                                     

       cJSON 開源項目位置:  http://sourceforge.net/projects/cjson/

       cJSON,目前來說,就只有兩個文件,一個cJSON.c 一個cJSON.h文件。使用的時候,自己創建好一個main.c文件后,如果是在linux pc上,請使用以下命令進行編譯:

 

1 gcc -g -Wall *.c -l m

就會默認生成一個 a.out文件,執行即可。在linux下編譯的時候,注意鏈接 libm 庫。

       整個項目都是以極標准的C來寫的,意思說,可以跨各種平台使用了。不過,還是有兩三處需要微調一下<針對stm32f103  + rt_thread >。其中修改一下malloc & free & size_t 這三個東西:

 46 static void *(*cJSON_malloc)(size_t sz) = malloc; 47 static void (*cJSON_free)(void *ptr) = free; ---------------------------------------- 46 static void *(*cJSON_malloc)(size_t sz) = rt_malloc; 47 static void (*cJSON_free)(void *ptr) = rt_free;
復制代碼
 60 void cJSON_InitHooks(cJSON_Hooks* hooks) 61 { 62 if (!hooks) { /* Reset hooks */ 63 cJSON_malloc = malloc; 64 cJSON_free = free; 65 return; 66 } 67 68 cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; 69 cJSON_free = (hooks->free_fn)?hooks->free_fn:free; 70 } ---------------------------------------------------- 60 void cJSON_InitHooks(cJSON_Hooks* hooks) 61 { 62 if (!hooks) { /* Reset hooks */ 63 cJSON_malloc = rt_malloc; 64 cJSON_free = rt_free; 65 return; 66 } 67 68 cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:rt_malloc; 69 cJSON_free = (hooks->free_fn)?hooks->free_fn:rt_free; 70 }
復制代碼

free & malloc就這么兩個地方要修改一下吧,其中size_t 這個應該是在 .h 文件修改,主要是rtt的 rt_malloc 和這里的malloc使用的不同,所以修改了下---不一定非要修改。

所以,這東西說實話,也不存在什么移植不移植的問題了。很輕松的就可以在各個平台使用了。

                                                                                                   例       程                                                                                                          

      不對json的格式進行說明了,下面直接記下使用方法了。

      第一,創建json數據串。這數據串,可能是對象,也可能是數組,也可能是它們的各種組合,其中再加上一些鍵值對。有一點要先說明:它們的組合,符合父子繼承格式--這也是json數據串的特點。

     <1>  創建一個對象,並在這個對象里面添加一個字符串鍵值和一個數字鍵值:

復制代碼
 1 int create_js(void)  2 {  3 cJSON *root;  4 /*create json string root*/  5 root = cJSON_CreateObject();  6 if(!root) {  7 DEBUG("get root faild !\n");  8 goto EXIT;  9 }else DEBUG("get root success!\n"); 10 11  { 12 cJSON * js_body ; 13 14 const char *const body = "body"; 15 cJSON_AddItemToObject(root, body, js_body=cJSON_CreateObject()); 16 cJSON_AddStringToObject(js_body,"name","xiaohui"); 17 cJSON_AddNumberToObject(js_body,"value",600); 18  { 19 char *s = cJSON_PrintUnformatted(root); 20 if(s){ 21 DEBUG("create js string is %s\n",s); 22  free(s); 23  } 24  } 25  cJSON_Delete(root); 26  } 27 28 return 0; 29 EXIT: 30 return -1; 31 } 32 33 int main(int argc, char **argv) 34 { 35  create_js(); 36 return 0; 37 }
復制代碼

運行結果:

1 create js string is {"body":{"name":"xiaohui","value":600}}

說明: 創建根對象,使用  cJSON_CreateObject(); 這個API,返回的是一個 cJSON的指針,注意,在這個指針用完了以后,需要手工調用 cJSON_Delete(root); 進行內存回收。

創建body對象的時候,是在根對象的基礎上進行創建,而插入name 和value的時候,是以body為父節點。需要注意的是  json 格式的數據,雖然也是一個字符串的樣子,但這個時候還是無法當成普通的字符串進行使用,需要調用 cJSON_PrintUnformatted(root) 或者 cJSON_Print(root);來將json對象轉換成普通的字符串,並且都是以該json對象的根為基點。兩個API的區別即是:一個是沒有格式的:也就是轉換出的字符串中間不會有"\n" "\t"之類的東西存在,而cJSON_Print(root);打印出來是人看起來很舒服的格式。僅此而已。

<2> 創建一個數組,並向數組添加一個字符串和一個數字:

復制代碼
 1 int create_js(void)  2 {  3 cJSON *root, *js_body;  4 root = cJSON_CreateArray();  5 cJSON_AddItemToArray(root, cJSON_CreateString("Hello world"));  6 cJSON_AddItemToArray(root, cJSON_CreateNumber(10));  7  {  8 // char *s = cJSON_Print(root);  9 char *s = cJSON_PrintUnformatted(root); 10 if(s){ 11 DEBUG(" %s \n",s); 12  free(s); 13  } 14  } 15 if(root) 16  cJSON_Delete(root); 17 18 return 0; 19 EXIT: 20 return -1; 21 } 22 23 int main(int argc, char **argv) 24 { 25  create_js(); 26 return 0; 27 }
復制代碼

運行結果:

1 ["Hello world",10]

<3> 對象里面包括一個數組,數組里面包括對象,對象里面再添加一個字符串和一個數字:

復制代碼
 1 int create_js(void)  2 {  3 cJSON *root, *js_body, *js_list;  4 root = cJSON_CreateObject();  5 cJSON_AddItemToObject(root,"body", js_body = cJSON_CreateArray());  6 cJSON_AddItemToArray(js_body, js_list = cJSON_CreateObject());  7 cJSON_AddStringToObject(js_list,"name","xiaohui");  8 cJSON_AddNumberToObject(js_list,"status",100);  9 10  { 11 // char *s = cJSON_Print(root); 12 char *s = cJSON_PrintUnformatted(root); 13 if(s){ 14 DEBUG(" %s \n",s); 15  free(s); 16  } 17  } 18 if(root) 19  cJSON_Delete(root); 20 21 return 0; 22 EXIT: 23 return -1; 24 } 25 26 int main(int argc, char **argv) 27 { 28  create_js(); 29 return 0; 30 }
復制代碼

運行結果:

1 {"body":[{"name":"xiaohui","status":100}]}

<4>其他組合就依次類推,只要搞清楚父子關系即可。隨便嵌套隨便玩。不再貼了。

   <第二, 解析json數據串>

   步驟: 1  先將普通的json 字符串 處理成 json對象格式。 2  根據關鍵字進行解析,解析的時候,需要根據關鍵字值的類型進行判斷,而這些類型,已經在cJSON.h里面寫明白了,包括:對象、數組、普通字符串、普通變量等等。

   <偷個懶吧,將自己學習的時候用的資料現貼過來,后面休息一下再詳細補充自己在工程中的解析方法>

http://blog.csdn.net/xukai871105/article/details/17094113

http://blog.sina.com.cn/s/blog_a6fb6cc90101ffme.html

 

<當然,他寫的比較簡潔,還有些可以補充的---其實我已經在上面使用文字進行補充過了。當然,不同的工程,可能解析的方法和側重點並不相同>

 

 

或許,是時候把解析的部分補充上來了:

處理流程:

1, 先將普通的json串處理成json對象,也就是所謂的創建json root的過程,只有一行代碼即可:

cJSON *root;
root = cJSON_Parse(js_string);

ps:需要注意的是,這個root在解析完成后,需要釋放掉,代碼如下:

if (root) cJSON_Delete(root);

2,開始拿關鍵字,但如果關鍵字還有父層或者祖層,那就需要先從父層開拿,所謂剝洋蔥是也!

先說沒有父層的:

{"name":"xiaohong","age":10}

這個字符串這樣拿即可:

復制代碼
 1 char *s = "{\"name\":\"xiao hong\",\"age\":10}";  2 cJSON *root = cJSON_Parse(s);  3 if(!root) {  4 printf("get root faild !\n");  5 return -1;  6 }  7 cJSON *name = cJSON_GetObjectItem(root, "name");  8 if(!name) {  9 printf("No name !\n"); 10 return -1; 11 } 12 printf("name type is %d\n",name->type); 13 printf("name is %s\n",name->valuestring); 14 15 cJSON *age = cJSON_GetObjectItem(root, "age"); 16 if(!age) { 17 printf("no age!\n"); 18 return -1; 19 } 20 printf("age type is %d\n", age->type); 21 printf("age is %d\n",age->valueint);
復制代碼

顯示結果:

1 name type is 4 2 name is xiao hong 3 age type is 3 4 age is 10

需要注意的是: 上面的type 已經在cJSON.h里面定義好了,有自己的意義。如果是在嚴格的場所,應該先判定該 item的type,然后再考慮去拿值。

而如果有父層的話,無非就是接着向下拿就是了,稍微修改下前面的demo吧:

處理這串數據吧:

{\"list\":{\"name\":\"xiao hong\",\"age\":10},\"other\":{\"name\":\"hua hua\"}}
復制代碼
 1 char *s = "{\"list\":{\"name\":\"xiao hong\",\"age\":10},\"other\":{\"name\":\"hua hua\"}}";  2 cJSON *root = cJSON_Parse(s);  3 if(!root) {  4 printf("get root faild !\n");  5 return -1;  6 }  7  8 cJSON *js_list = cJSON_GetObjectItem(root, "list");  9 if(!js_list) { 10 printf("no list!\n"); 11 return -1; 12 } 13 printf("list type is %d\n",js_list->type); 14 15 cJSON *name = cJSON_GetObjectItem(js_list, "name"); 16 if(!name) { 17 printf("No name !\n"); 18 return -1; 19 } 20 printf("name type is %d\n",name->type); 21 printf("name is %s\n",name->valuestring); 22 23 cJSON *age = cJSON_GetObjectItem(js_list, "age"); 24 if(!age) { 25 printf("no age!\n"); 26 return -1; 27 } 28 printf("age type is %d\n", age->type); 29 printf("age is %d\n",age->valueint); 30 31 cJSON *js_other = cJSON_GetObjectItem(root, "other"); 32 if(!js_other) { 33 printf("no list!\n"); 34 return -1; 35 } 36 printf("list type is %d\n",js_other->type); 37 38 cJSON *js_name = cJSON_GetObjectItem(js_other, "name"); 39 if(!js_name) { 40 printf("No name !\n"); 41 return -1; 42 } 43 printf("name type is %d\n",js_name->type); 44 printf("name is %s\n",js_name->valuestring); 45 46 if(root) 47  cJSON_Delete(root); 48 return 0;
復制代碼

打印結果:

復制代碼
1 list type is 6 2 name type is 4 3 name is xiao hong 4 age type is 3 5 age is 10 6 list type is 6 7 name type is 4 8 name is hua hua
復制代碼

所謂子子孫孫無窮盡也,按照上面那個方法推下去即可。

3,json 里數組怎么取?

1 {\"list\":[\"name1\",\"name2\"]}

代碼如下:

復制代碼
 1 int main(int argc, char **argv)  2 {  3 char *s = "{\"list\":[\"name1\",\"name2\"]}";  4 cJSON *root = cJSON_Parse(s);  5 if(!root) {  6 printf("get root faild !\n");  7 return -1;  8 }  9 cJSON *js_list = cJSON_GetObjectItem(root, "list"); 10 if(!js_list){ 11 printf("no list!\n"); 12 return -1; 13 } 14 int array_size = cJSON_GetArraySize(js_list); 15 printf("array size is %d\n",array_size); 16 int i = 0; 17 cJSON *item; 18 for(i=0; i< array_size; i++) { 19 item = cJSON_GetArrayItem(js_list, i); 20 printf("item type is %d\n",item->type); 21 printf("%s\n",item->valuestring); 22 } 23 24 if(root) 25  cJSON_Delete(root); 26 return 0; 27 }
復制代碼

對頭,好簡單的樣子<在別人的庫上使用>

 

4  如果json數組里面又搞了對象怎么辦? 

不怕搞對象,就怕這樣搞對象? 無他,就是稍微復雜了一點,全是體力活兒:

<1> 既然是數組里面,那肯定要先測量數組的大小,然后根據大小循環拿;

<2> 拿到一個數組項,然后把這個項先轉化成普通的json字符串,也就是 char *s 能接受的。

<3>再次將這個json字符串,轉化成一個json對象

<4> 按照拿普通對象中的東西那樣開干就行了。

ps:曾經試過直接在數組項中拿內容,失敗了,不知為何,於是就按照這個4步開干了。

比如:

1 {\"list\":[{\"name\":\"xiao hong\",\"age\":10},{\"name\":\"hua hua\",\"age\":11}]}

是的.list 是一個數組,數組里面有兩個對象,那么代碼如下:

復制代碼
 1 int main(int argc, char **argv)  2 {  3 char *s = "{\"list\":[{\"name\":\"xiao hong\",\"age\":10},{\"name\":\"hua hua\",\"age\":11}]}";  4 cJSON *root = cJSON_Parse(s);  5 if(!root) {  6 printf("get root faild !\n");  7 return -1;  8 }  9 cJSON *js_list = cJSON_GetObjectItem(root, "list"); 10 if(!js_list){ 11 printf("no list!\n"); 12 return -1; 13 } 14 int array_size = cJSON_GetArraySize(js_list); 15 printf("array size is %d\n",array_size); 16 int i = 0; 17 cJSON *item,*it, *js_name, *js_age; 18 char *p = NULL; 19 for(i=0; i< array_size; i++) { 20 item = cJSON_GetArrayItem(js_list, i); 21 if(!item) { 22 //TODO... 23  } 24 p = cJSON_PrintUnformatted(item);  /* JSON 格式化該子對象,以供 cJSON_Parse 解析對象,另一種方法就是去除 24-25行,26-31行 it 由 item 替換即可 */ 25 it = cJSON_Parse(p); 26 if(!it) 27 continue ; 28 js_name = cJSON_GetObjectItem(it, "name"); 29 printf("name is %s\n",js_name->valuestring); 30 js_age = cJSON_GetObjectItem(it, "age"); 31 printf("age is %d\n",js_age->valueint); 32 33 } 34 35 if(root) 36  cJSON_Delete(root); 37 return 0; 38 }
復制代碼

按理說,應該釋放下 it 變量才對,但似乎事實不是這樣,僅僅可以釋放根root。

好了,json 解析,無非就是上面的組合了,還能有什么呢?

 

解析和封裝都有了,此文結束了。

看這里:

https://github.com/boyisgood86/learning/tree/master/cjson

 

good luck !

 

 

update:  上面第四部分會導致內存泄露,修改方法見貼圖:

 

原文出處


免責聲明!

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



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