1. 程式人生 > 其它 >使用cJSON解析JSON字串

使用cJSON解析JSON字串

JSON學習-使用cJSON解析

使用cJSON解析JSON字串

一、為何選擇cJSON

         我們在使用JSON格式時,如果只是處理簡單的協議,可以依據JSON格式,通過對字串的操作來進行解析與建立。然而隨著協議逐漸複雜起來,經常會遇到一些未考慮周全的地方,需要進一步的完善解析方法,此時,使用比較完善的JSON解析庫的需求就提出來了。

         基於方便引用的考慮,我們希望這個JSON解析庫是用C語言實現的。同時,為了避免太過複雜的C原始碼包含關係,希望最好是一個C檔案來實現。通過在網路上的查詢,發現cJSON是比較符合要求的。cJSON只有一個C檔案,一個頭檔案,包含到專案原始碼中非常方便,而且其實現效率也是非常高的。

二、cJSON的核心結構體

         cJSON的核心結構體就是一個cJSON,理解了這個結構體,基本上對cJSON的使用就有了個基本概念了。該結構體具體定義如下:

typedef struct cJSON {
       struct cJSON*next,*prev;           /* 遍歷陣列或物件鏈的前向或後向連結串列指標*/
       struct cJSON *child;                   /*陣列或物件的孩子節點*/
       int type;                                     /* key的型別*/
       char *valuestring;                       /*字串值*/
       int valueint;                                /* 整數值*/
       double valuedouble;                    /* 浮點數值*/
       char *string;                               /* key的名字*/
} cJSON;

說明:

1、cJSON是使用連結串列來儲存資料的,其訪問方式很像一顆樹。每一個節點可以有兄弟節點,通過next/prev指標來查詢,它類似雙向連結串列;每個節點也可以有孩子節點,通過child指標來訪問,進入下一層。只有節點是物件或陣列時才可以有孩子節點。

2、type是鍵(key)的型別,一共有7種取值,分別是:False,Ture,NULL,Number,String,Array,Object。

若是Number型別,則valueint或valuedouble中儲存著值。若期望的是int,則訪問valueint,若期望的是double,則訪問valuedouble,可以得到值。

若是String型別的,則valuestring中儲存著值,可以訪問valuestring得到值。

3、string中存放的是這個節點的名字,可理解為key的名稱。

三、解析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介面的同學,可以有些借鑑參考的作用。 

附帶例程:

#include <stdio.h> 
#include <string.h> 
#include <sys/types.h> 
#include <stdlib.h> 
#include <unistd.h> 
 
#include "cJSON.h" 
 
typedef struct 
{  
 int id;  
 char firstName[32];  
 char lastName[32];  
 char email[64];  
 int age;  
 float height;  
}people;  
 
void dofile(char *filename);/* Read a file, parse, render back, etc. */ 
 
int main(int argc, char **argv)  
{  
 
//  dofile("json_str1.txt"); 
//  dofile("json_str2.txt"); 
    dofile("json_str3.txt");  
 
 return 0;  
}  
 
//parse a key-value pair 
int cJSON_to_str(char *json_string, char *str_val)  
{  
    cJSON *root=cJSON_Parse(json_string);  
 if (!root)  
    {  
        printf("Error before: [%s]n",cJSON_GetErrorPtr());  
 return -1;  
    }  
 else 
    {  
        cJSON *item=cJSON_GetObjectItem(root,"firstName");  
 if(item!=NULL)  
        {  
            printf("cJSON_GetObjectItem: type=%d, key is %s, value is %sn",item->type,item->string,item->valuestring);  
            memcpy(str_val,item->valuestring,strlen(item->valuestring));  
        }  
        cJSON_Delete(root);  
    }  
 return 0;  
}  
 
//parse a object to struct 
int cJSON_to_struct(char *json_string, people *person)  
{  
    cJSON *item;  
    cJSON *root=cJSON_Parse(json_string);  
 if (!root)  
    {  
        printf("Error before: [%s]n",cJSON_GetErrorPtr());  
 return -1;  
    }  
 else 
    {  
        cJSON *object=cJSON_GetObjectItem(root,"person");  
 if(object==NULL)  
        {  
            printf("Error before: [%s]n",cJSON_GetErrorPtr());  
            cJSON_Delete(root);  
 return -1;  
        }  
        printf("cJSON_GetObjectItem: type=%d, key is %s, value is %sn",object->type,object->string,object->valuestring);  
 
 if(object!=NULL)  
        {  
            item=cJSON_GetObjectItem(object,"firstName");  
 if(item!=NULL)  
            {  
                printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%sn",item->type,item->string,item->valuestring);  
                memcpy(person->firstName,item->valuestring,strlen(item->valuestring));  
            }  
 
            item=cJSON_GetObjectItem(object,"lastName");  
 if(item!=NULL)  
            {  
                printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%sn",item->type,item->string,item->valuestring);  
                memcpy(person->lastName,item->valuestring,strlen(item->valuestring));  
            }  
 
            item=cJSON_GetObjectItem(object,"email");  
 if(item!=NULL)  
            {  
                printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%sn",item->type,item->string,item->valuestring);  
                memcpy(person->email,item->valuestring,strlen(item->valuestring));  
            }  
 
            item=cJSON_GetObjectItem(object,"age");  
 if(item!=NULL)  
            {  
                printf("cJSON_GetObjectItem: type=%d, string is %s, valueint=%dn",item->type,item->string,item->valueint);  
                person->age=item->valueint;  
            }  
 else 
            {  
                printf("cJSON_GetObjectItem: get age failedn");  
            }  
 
            item=cJSON_GetObjectItem(object,"height");  
 if(item!=NULL)  
            {  
                printf("cJSON_GetObjectItem: type=%d, string is %s, valuedouble=%fn",item->type,item->string,item->valuedouble);  
                person->height=item->valuedouble;  
            }  
        }  
 
        cJSON_Delete(root);  
    }  
 return 0;  
}  
 
//parse a struct array 
int cJSON_to_struct_array(char *text, people worker[])  
{  
    cJSON *json,*arrayItem,*item,*object;  
 int i;  
 
    json=cJSON_Parse(text);  
 if (!json)  
    {  
        printf("Error before: [%s]n",cJSON_GetErrorPtr());  
    }  
 else 
    {  
        arrayItem=cJSON_GetObjectItem(json,"people");  
 if(arrayItem!=NULL)  
        {  
 int size=cJSON_GetArraySize(arrayItem);  
            printf("cJSON_GetArraySize: size=%dn",size);  
 
 for(i=0;i<size;i++)  
            {  
                printf("i=%dn",i);  
                object=cJSON_GetArrayItem(arrayItem,i);  
 
                item=cJSON_GetObjectItem(object,"firstName");  
 if(item!=NULL)  
                {  
                    printf("cJSON_GetObjectItem: type=%d, string is %sn",item->type,item->string);  
                    memcpy(worker[i].firstName,item->valuestring,strlen(item->valuestring));  
                }  
 
                item=cJSON_GetObjectItem(object,"lastName");  
 if(item!=NULL)  
                {  
                    printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%sn",item->type,item->string,item->valuestring);  
                    memcpy(worker[i].lastName,item->valuestring,strlen(item->valuestring));  
                }  
 
                item=cJSON_GetObjectItem(object,"email");  
 if(item!=NULL)  
                {  
                    printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%sn",item->type,item->string,item->valuestring);  
                    memcpy(worker[i].email,item->valuestring,strlen(item->valuestring));  
                }  
 
                item=cJSON_GetObjectItem(object,"age");  
 if(item!=NULL)  
                {  
                    printf("cJSON_GetObjectItem: type=%d, string is %s, valueint=%dn",item->type,item->string,item->valueint);  
                    worker[i].age=item->valueint;  
                }  
 else 
                {  
                    printf("cJSON_GetObjectItem: get age failedn");  
                }  
 
                item=cJSON_GetObjectItem(object,"height");  
 if(item!=NULL)  
                {  
                    printf("cJSON_GetObjectItem: type=%d, string is %s, value=%fn",item->type,item->string,item->valuedouble);  
                    worker[i].height=item->valuedouble;  
                }  
            }  
        }  
 
 for(i=0;i<3;i++)  
        {  
            printf("i=%d, firstName=%s,lastName=%s,email=%s,age=%d,height=%fn",  
                    i,  
                    worker[i].firstName,  
                    worker[i].lastName,  
                    worker[i].email,  
                    worker[i].age,  
                    worker[i].height);  
        }  
 
        cJSON_Delete(json);  
    }  
 return 0;  
}  
 
// Read a file, parse, render back, etc. 
void dofile(char *filename)  
{  
 FILE *f;  
 int len;  
 char *data;  
 
    f=fopen(filename,"rb");  
    fseek(f,0,SEEK_END);  
    len=ftell(f);  
    fseek(f,0,SEEK_SET);  
    data=(char*)malloc(len+1);  
    fread(data,1,len,f);  
    fclose(f);  
 
    printf("read file %s complete, len=%d.n",filename,len);  
 
//  char str_name[40]; 
//  int ret = cJSON_to_str(data, str_name); 
 
//  people person; 
//  int ret = cJSON_to_struct(data, &person); 
 
    people worker[3]={{0}};  
    cJSON_to_struct_array(data, worker);  
 
    free(data);  
}