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);
-
-
int main(int argc, char **argv)
-
{
-
-
-
-
dofile("json_str3.txt");
-
-
return 0;
-
}
-
-
-
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 %s\n",item->type,item->string,item->valuestring);
-
memcpy(str_val,item->valuestring,strlen(item->valuestring));
-
}
-
cJSON_Delete(root);
-
}
-
return 0;
-
}
-
-
-
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 %s\n",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=%s\n",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=%s\n",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=%s\n",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=%d\n",item->type,item->string,item->valueint);
-
person->age=item->valueint;
-
}
-
else
-
{
-
printf("cJSON_GetObjectItem: get age failed\n");
-
}
-
-
item=cJSON_GetObjectItem(object,"height");
-
if(item!=NULL)
-
{
-
printf("cJSON_GetObjectItem: type=%d, string is %s, valuedouble=%f\n",item->type,item->string,item->valuedouble);
-
person->height=item->valuedouble;
-
}
-
}
-
-
cJSON_Delete(root);
-
}
-
return 0;
-
}
-
-
-
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=%d\n",size);
-
-
for(i=0;i<size;i++)
-
{
-
printf("i=%d\n",i);
-
object=cJSON_GetArrayItem(arrayItem,i);
-
-
item=cJSON_GetObjectItem(object,"firstName");
-
if(item!=NULL)
-
{
-
printf("cJSON_GetObjectItem: type=%d, string is %s\n",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=%s\n",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=%s\n",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=%d\n",item->type,item->string,item->valueint);
-
worker[i].age=item->valueint;
-
}
-
else
-
{
-
printf("cJSON_GetObjectItem: get age failed\n");
-
}
-
-
item=cJSON_GetObjectItem(object,"height");
-
if(item!=NULL)
-
{
-
printf("cJSON_GetObjectItem: type=%d, string is %s, value=%f\n",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=%f\n",
-
i,
-
worker[i].firstName,
-
worker[i].lastName,
-
worker[i].email,
-
worker[i].age,
-
worker[i].height);
-
}
-
-
cJSON_Delete(json);
-
}
-
return 0;
-
}
-
-
-
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);
-
-
-
-
-
-
-
-
people worker[3]={{0}};
-
cJSON_to_struct_array(data, worker);
-
-
free(data);
-
}