1.json簡介
2.cjson介紹
3.cjson應用實例
3.0.實例源碼下載
3.1.json標準格式
標準JSON的合法符號:{(左大括號) }(右大括號) "(雙引號) :(冒號) ,(逗號) [(左中括號) ](右中括號)
JSON字符串:特殊字符可在字符前面加 \ 或使用 \u 加 4位16進制數來處理
- {"name":"jobs"}
JSON布爾:必須小寫的true和false
- {"bool":true}
JSON空:必須小寫的null
- {"object":null}
JSON數值:不能使用8/16進制
- {"num":60}
- {"num":-60}
- {"num":6.6666}
- {"num":1e+6}<!-- 1乘10的6次方,e不區分大小寫 -->
- {"num":1e-6}<!-- 1乘10的負6次方,e不區分大小寫 -->
JSON對象
- {
- "starcraft": {
- "INC": "Blizzard",
- "price": 60
- }
- }
JSON數組
- {
- "person": [
- "jobs",
- 60
- ]
- }
JSON對象數組
- {
- "array": [
- {
- "name": "jobs"
- },
- {
- "name": "bill",
- "age": 60
- },
- {
- "product": "war3",
- "type": "game",
- "popular": true,
- "price": 60
- }
- ]
- }
參考: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的同學,可以有些借鑑參考的作用。
附帶例程:
- #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_str4.txt");
- return 0;
- }
- //create a key-value pair
- int str_to_cJSON(char *json_string, char *str_val)
- {
- char * out=NULL;
- cJSON *root=cJSON_CreateObject();
- if (!root)
- {
- printf("Error before: [%s]\n",cJSON_GetErrorPtr());
- return -1;
- }
- else
- {
- cJSON *item=cJSON_CreateString("Brett");
- cJSON_AddItemToObject(root,"firstName",item);
- out=cJSON_Print(root);
- printf("out2:%s\n",out);
- cJSON_Delete(root);
- if(out!=NULL)
- {
- free(out);
- }
- }
- return 0;
- }
- //create a object from struct
- int struct_to_cJSON(char *json_string, people *person)
- {
- if((json_string==NULL) || (person==NULL))
- {
- printf("%s: input is invalid",__func__);
- }
- char * out=NULL;
- cJSON *root=cJSON_CreateObject();
- if (!root)
- {
- printf("Error before: [%s]\n",cJSON_GetErrorPtr());
- return -1;
- }
- else
- {
- cJSON *obj_person=cJSON_CreateObject();
- 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);
- cJSON_AddItemToObject(root,"person",obj_person);
- out=cJSON_Print(root);
- printf("out2:%s\n",out);
- cJSON_Delete(root);
- if(out!=NULL)
- {
- memcpy(json_string,out,strlen(out));
- free(out);
- }
- }
- return 0;
- }
- //a struct array to CJSON
- int struct_array_to_cJSON(char *text, people worker[])
- {
- cJSON *json,*arrayItem,*item,*object;
- int i;
- 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);
- }
- if((text==NULL) || (worker==NULL))
- {
- printf("%s: input is invalid",__func__);
- }
- char * out=NULL;
- cJSON *root=cJSON_CreateObject();
- if (!root)
- {
- printf("Error before: [%s]\n",cJSON_GetErrorPtr());
- return -1;
- }
- else
- {
- cJSON *array_person=cJSON_CreateArray();
- for(i=0;i<3;i++)
- {
- cJSON *obj_person=cJSON_CreateObject();
- cJSON *item=cJSON_CreateString(worker[i].firstName);
- cJSON_AddItemToObject(obj_person,"firstName",item);
- item=cJSON_CreateString(worker[i].lastName);
- cJSON_AddItemToObject(obj_person,"lastName",item);
- item=cJSON_CreateString(worker[i].email);
- cJSON_AddItemToObject(obj_person,"email",item);
- item=cJSON_CreateNumber(worker[i].age);
- cJSON_AddItemToObject(obj_person,"age",item);
- item=cJSON_CreateNumber(worker[i].height);
- cJSON_AddItemToObject(obj_person,"height",item);
- cJSON_AddItemToArray(array_person,obj_person);
- }
- cJSON_AddItemToObject(root,"people",array_person);
- out=cJSON_Print(root);
- printf("out:%s\n",out);
- cJSON_Delete(root);
- if(out!=NULL)
- {
- memcpy(text,out,strlen(out));
- free(out);
- }
- }
- return 0;
- }
- // create CJSON, write file
- void dofile(char *filename)
- {
- FILE *f;
- int len;
- char data[1024];
- f=fopen(filename,"wb");
- fseek(f,0,SEEK_END);
- len=ftell(f);
- fseek(f,0,SEEK_SET);
- printf("read file %s complete, len=%d.\n",filename,len);
- // char str_name[40];
- // int ret = str_to_cJSON(data, str_name);
- people worker[3]={
- {0,"zhong","Jason","[email protected]",0,1.67},
- {1,"","jadena","[email protected]",8,1.17},
- {2,"zhu","Juliet","[email protected]",36,1.55}
- };
- // struct_to_cJSON(data, &worker[1]);
- struct_array_to_cJSON(data, worker);
- fwrite(data,1,strlen(data),f);
- fclose(f);
- }
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接口的同學,可以有些借鑑參考的作用。
附帶例程:
- #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 %s\n",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 %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;
- }
- //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=%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;
- }
- // 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);
- }
4.cjson源碼解析
4.1.cjson基本數據結構
/* cJSON Types: */
enum {cJSON_False, cJSON_True, cJSON_NULL, cJSON_Number, cJSON_String, cJSON_Array, cJSON_Object, cJSON_IsReference=256};
/* 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;
4.2.節點操作
4.2.1.json 內存管理--hook 管理函數
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.創建節點
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node,0,sizeof(cJSON));
return node;
}
/* 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;}
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); // 添加兒子
}
4.2.6.刪除兒子節點
// 節點從雙向鏈表中刪除即可
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);
}
/* 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;
}
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.解析對象
/* 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;
}