C/C 關(guān)于 cJson 庫的使用
時間:2021-09-24 15:57:00
手機看文章
掃描二維碼
隨時隨地手機看文章
[導(dǎo)讀]關(guān)于Json這種數(shù)據(jù)格式,在前面已經(jīng)做了詳細的介紹?Json的格式和用途,在項目開發(fā)過程中我們需要針對不同的語言使用不同的庫對Json格式的數(shù)據(jù)進行解析,下面給大家介紹一個基于C語言的Json庫–cJson。cJSON是一個超輕巧,攜帶方便,單文件,簡單的可以作為ANSI-C標準...
關(guān)于 Json 這種數(shù)據(jù)格式,在前面已經(jīng)做了詳細的介紹 Json 的格式和用途,在項目開發(fā)過程中我們需要針對不同的語言使用不同的庫對 Json 格式的數(shù)據(jù)進行解析,下面給大家介紹一個基于 C 語言的 Json 庫 – cJson。cJSON 是一個超輕巧,攜帶方便,單文件,簡單的可以作為 ANSI-C 標準的 JSON 解析器。
cJSON,目前來說,主要的文件有兩個,一個
cJSON.c一個 cJSON.h。使用的時候,將頭文件 include 進去即可。
如果是在 Linux 操作系統(tǒng)中使用,編譯 到時候需要添加數(shù)學(xué)庫 libm.so,如下所示:
gcc *.c cJSON.c -lm
1. cJSON 結(jié)構(gòu)體
在 cJSON.h中定義了一個非常重要的結(jié)構(gòu)體 cJSON,想要熟悉使用 cJSON 庫函數(shù)可從 cJSON 結(jié)構(gòu)體入手,cJSON 結(jié)構(gòu)體如下所示:
typedef struct cJSON {
struct cJSON *next,*prev;
struct cJSON *child;
int type;
char *valuestring; // value值是字符串類型
int valueint;
double valuedouble;
char *string; // 對象中的key
} cJSON;
關(guān)于這個結(jié)構(gòu)體做如下幾點的說明:
-
cJOSN 結(jié)構(gòu)體是一個雙向鏈表,并且可通過 child 指針訪問下一層。
-
結(jié)構(gòu)體成員 type 變量用于描述數(shù)據(jù)元素的類型(如果是鍵值對表示 value 值的類型),數(shù)據(jù)元素可以是字符串可以是整形,也可以是浮點型。
-
如果是整形值的話可通過 valueint 將值取出
-
如果是浮點型的話可通過 valuedouble 將值取出
-
如果是字符串類型的話可通過 valuestring 將值取出
-
結(jié)構(gòu)體成員 string 表示鍵值對中鍵值的名稱。
cJSON 作為 Json 格式的解析庫,其主要功能就是構(gòu)建和解析 Json 格式了,比如要發(fā)送數(shù)據(jù):用途就是發(fā)送端將要發(fā)送的數(shù)據(jù)以 json 形式封裝,然后發(fā)送,接收端收到此數(shù)據(jù)后,還是按 json 形式解析,就得到想要的數(shù)據(jù)了。
2. cJson API
Json 格式的數(shù)據(jù)無外乎有兩種 Json對象和 Json數(shù)組,創(chuàng)建的 Json 數(shù)據(jù)串可能是二者中 的一種,也可能是二者的組合,不管哪一種通過調(diào)用相關(guān)的 API 函數(shù)都可以輕松的做到這一點。
2.1 數(shù)據(jù)的封裝
在 cJSON.h頭文件中可以看到一些函數(shù)聲明,通過調(diào)用這些創(chuàng)建函數(shù)就可以將 Json 支持的數(shù)據(jù)類型封裝為 cJSON 結(jié)構(gòu)體類型:
// 空值類型
extern cJSON *cJSON_CreateNull(void);
// 布爾類型
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
// 數(shù)值類型
extern cJSON *cJSON_CreateNumber(double num);
// 字符串類型
extern cJSON *cJSON_CreateString(const char *string);
// json數(shù)組(創(chuàng)建空數(shù)組)
extern cJSON *cJSON_CreateArray(void);
// json對象(創(chuàng)建空對象)
extern cJSON *cJSON_CreateObject(void);
另外,cJson 庫中還給我我們提供了一些更為簡便的操作函數(shù),在創(chuàng)建數(shù)組的同時還可以進行初始化
// 創(chuàng)建一個Json數(shù)組, 元素為整形
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
// 創(chuàng)建一個Json數(shù)組, 元素為浮點
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
// 創(chuàng)建一個Json數(shù)組, 元素為字符串類型
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
2.2 Json 對象操作
當(dāng)?shù)玫揭粋€ Json 對象之后,就可以往對象中添加鍵值對了,可以使用 cJSON_AddItemToObject()
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
在 cJSON 庫中節(jié)點的從屬關(guān)系是通過樹來維護的,每一層節(jié)點都是通過鏈表來維護的,這樣就能分析出該函數(shù)參數(shù)的含義:
-
object:要添加的鍵值對從屬于那個節(jié)點
-
string:添加的鍵值對的鍵值
-
item:添加的鍵值對的 value 值(需要先將其封裝為 cJSON 類型的結(jié)構(gòu)體)
為了讓我的操作更加方便,cJson 庫還給我們提供了一些宏函數(shù),方便我們快速的往 Json 對象中添加鍵值對
#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))
我們還可以根據(jù) Json 對象中的鍵值取出相應(yīng)的 value 值,API 函數(shù)原型如下:
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
2.3 Json 數(shù)組操作
添加數(shù)據(jù)到 Json 數(shù)組中(原始數(shù)據(jù)需要先轉(zhuǎn)換為 cJSON 結(jié)構(gòu)體類型)
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
得到 Json 數(shù)組中元素的個數(shù):
extern int cJSON_GetArraySize(cJSON *array);
得到 Json 數(shù)組中指定位置的原素,如果返回 NULL 表示取值失敗了。
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
2.4 序列化
序列化就是將 Json 格式的數(shù)據(jù)轉(zhuǎn)換為字符串的過程,cJson 庫中給我們提供了 3 個轉(zhuǎn)換函數(shù),具體如下:
第一個參數(shù) item 表示 Json 數(shù)據(jù)塊的根節(jié)點。
extern char *cJSON_Print(cJSON *item);
extern char *cJSON_PrintUnformatted(cJSON *item);
extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
-
調(diào)用
cJSON_Print()函數(shù)我們可以得到一個帶格式的 Json 字符串(有換行,看起來更直觀)
-
調(diào)用 cJSON_PrintUnformatted()函數(shù)會得到一個沒有格式的 Json 字符串(沒有換行,所有的數(shù)據(jù)都在同一行)。
-
調(diào)用 cJSON_PrintBuffered()函數(shù)使用緩沖策略將 Json 實體轉(zhuǎn)換為字符串,參數(shù) prebuffer 是指定緩沖區(qū)的大小,參數(shù) fmt==0表示未格式化,fmt==1表示格式化。
我們在編碼過程中可以根據(jù)自己的實際需求調(diào)用相關(guān)的操作函數(shù)得到對應(yīng)格式的 Json 字符串。
2.5 Json 字符串的解析
如果我們得到了一個 Json 格式的字符串,想要讀出里邊的數(shù)據(jù),就需要對這個字符串進行解析,處理方式就是將字符串轉(zhuǎn)換為 cJSON 結(jié)構(gòu)體,然后再基于這個結(jié)構(gòu)體讀里邊的原始數(shù)據(jù),轉(zhuǎn)換函數(shù)的函數(shù)原型如下:
extern cJSON *cJSON_Parse(const char *value);
2.6 內(nèi)存釋放
當(dāng)我們將數(shù)據(jù)封裝為 cJSON 結(jié)構(gòu)類型的節(jié)點之后都會得到一塊堆內(nèi)存,當(dāng)我們釋放某個節(jié)點的時候可以調(diào)用 cJson 庫提供的刪除函數(shù) cJSON_Delete(),函數(shù)原型如下:
extern void cJSON_Delete(cJSON *c);
該函數(shù)的參數(shù)為要釋放的節(jié)點的地址,在此強調(diào)一點:在進行內(nèi)存地址釋放的時候,當(dāng)前節(jié)點以及其子節(jié)點都會被刪除。
3. Json 數(shù)據(jù)的封裝
3.1 Json 對象操作舉例
創(chuàng)建一個對象,并向這個對象里添加字符串和整型鍵值:
#include
#include
#include
#include"cJSON.h"
int main()
{
cJSON * root;
cJSON *arry;
root=cJSON_CreateObject(); // 創(chuàng)建根數(shù)據(jù)對象
cJSON_AddStringToObject(root,"name","luffy"); // 添加鍵值對
cJSON_AddStringToObject(root,"sex","man"); // 添加鍵值對
cJSON_AddNumberToObject(root,"age",19); // 添加鍵值對
char *out = cJSON_Print(root); // 將json形式轉(zhuǎn)換成字符串
printf("%s\n",out);
// 釋放內(nèi)存
cJSON_Delete(root);
free(out);
}
運行結(jié)果
{
"name": "luffy",
"sex": "man",
"age": 19
}
若干說明:
-
cJSON_CreateObject函數(shù)可創(chuàng)建一個根對象,返回的是一個 cJSON 指針,在這個指針用完了以后,需要手動調(diào)用 cJSON_Delete(root)進行內(nèi)存回收。
-
函數(shù) cJSON_Print()內(nèi)部封裝了 malloc 函數(shù),所以需要使用 free() 函數(shù)釋放被 out 占用的內(nèi)存空間。
3.2 Json 數(shù)組操作舉例
創(chuàng)建一個數(shù)組,并向數(shù)組添加一個字符串和一個數(shù)字
int main(int argc, char **argv)
{
cJSON *root;
root = cJSON_CreateArray();
cJSON_AddItemToArray(root, cJSON_CreateString("Hello world"));
cJSON_AddItemToArray(root, cJSON_CreateNumber(10));
// char *s = cJSON_Print(root);
char *s = cJSON_PrintUnformatted(root);
if(s)
{
printf(" %s \n",s);
free(s);
}
cJSON_Delete(root);
return 0;
}
運行結(jié)果:
["Hello world",10]
3.3 Json 對象、數(shù)組嵌套使用
對象里面包括一個數(shù)組,數(shù)組里面包括對象,對象里面再添加一個字符串和一個數(shù)字
{
"person":[{
"name":"luffy",
"age":19
}]
}
示例代碼:
int main(int argc, char **argv)
{
cJSON *root, *body, *list;
// josn 對象 root
root = cJSON_CreateObject();
// root 添加鍵值對 person:json數(shù)組A
cJSON_AddItemToObject(root,"person", body = cJSON_CreateArray());
// json數(shù)組A 添加Json對象B
cJSON_AddItemToArray(body, list = cJSON_CreateObject());
// 在json對象B中添加鍵值對: "name":"luffy"
cJSON_AddStringToObject(list,"name","luffy");
// 在json對象B中添加鍵值對: "age":19
cJSON_AddNumberToObject(list,"age",19);
// char *s = cJSON_Print(root);
char *s = cJSON_PrintUnformatted(root);
if(s)
{
printf(" %s \n",s);
free(s);
}
if(root)
{
cJSON_Delete(root);
}
return 0;
}
運行結(jié)果:
{"person":[{"name":"luffy","age":19}]}
4. 解析 Json 字符串
4.1 解析 Json 對象
Json 字符串的解析流程和數(shù)據(jù)的封裝流程相反,假設(shè)我們有這樣一個 Json 字符串(字符串中的雙引號需要通過轉(zhuǎn)義字符將其轉(zhuǎn)譯為普通字符):
{\"name\":\"luffy\",\"sex\":\"man\",\"age\":19}
示例代碼如下:
#include
#include
#include
#include "cJSON.h"
int main()
{
cJSON *json, *name, *sex, *age;
char* out="{\"name\":\"luffy\",\"sex\":\"man\",\"age\":19}";
json = cJSON_Parse(out); //解析成json形式
name = cJSON_GetObjectItem(json, "name"); //獲取鍵值內(nèi)容
sex = cJSON_GetObjectItem(json, "sex");
age = cJSON_GetObjectItem(json, "age");
printf("name:%s,sex:%s,age:%d\n", name->valuestring, sex->valuestring, age->valueint);
cJSON_Delete(json); //釋放內(nèi)存
}
輸出的結(jié)果:
name:luffy,sex:man,age:19
如果是在嚴格的場所,應(yīng)該先判定每個 item 的 type,然后再考慮去取值。
4.2 解析嵌套的 Json 對象
加大一點難度,下面我們解析一個嵌套的 Json 對象,數(shù)據(jù)如下:
{\"list\":{\"name\":\"luffy\",\"age\":19},\"other\":{\"name\":\"ace\"}}
int main()
{
char *s = "{\"list\":{\"name\":\"luffy\",\"age\":19},\"other\":{\"name\":\"ace\"}}";
cJSON *root = cJSON_Parse(s);
if(!root)
{
printf("get root faild !\n");
return -1;
}
cJSON *js_list = cJSON_GetObjectItem(root, "list");
if(!js_list)
{
printf("no list!\n");
return -1;
}
printf("list type is %d\n",js_list->type);
cJSON *name = cJSON_GetObjectItem(js_list, "name");
if(!name)
{
printf("No name !\n");
return -1;
}
printf("name type is %d\n",name->type);
printf("name is %s\n",name->valuestring);
cJSON *age = cJSON_GetObjectItem(js_list, "age");
if(!age)
{
printf("no age!\n");
return -1;
}
printf("age type is %d\n", age->type);
printf("age is %d\n",age->valueint);
cJSON *js_other = cJSON_GetObjectItem(root, "other");
if(!js_other)
{
printf("no list!\n");
return -1;
}
printf("list type is %d\n",js_other->type);
cJSON *js_name = cJSON_GetObjectItem(js_other, "name");
if(!js_name)
{
printf("No name !\n");
return -1;
}
printf("name type is %d\n",js_name->type);
printf("name is %s\n",js_name->valuestring);
if(root)
{
cJSON_Delete(root);
}
return 0;
}
打印結(jié)果:
list type is 6
name type is 4
name is luffy
age type is 3
age is 19
list type is 6
name type is 4
name is ace
4.3 解析 Json 數(shù)組
如果我們遇到的 Json 字符串是一個 Json 數(shù)組格式,處理方式和 Json 對象差不多,比如我們要解析如下字符串:
{\"names\":[\"luffy\",\"robin\"]}
int main(int argc, char **argv)
{
char *s = "{\"names\":[\"luffy\",\"robin\"]}";
cJSON *root = cJSON_Parse(s);
if(!root)
{
printf("get root faild !\n");
return -1;
}
cJSON *js_list = cJSON_GetObjectItem(root, "names");
if(!js_list)
{
printf("no list!\n");
return -1;
}
int array_size = cJSON_GetArraySize(js_list);
printf("array size is %d\n",array_size);
for(int i=0; i< array_size; i )
{
cJSON *item = cJSON_GetArrayItem(js_list, i);
printf("item type is %d\n",item->type);
printf("%s\n",item->valuestring);
}
if(root)
{
cJSON_Delete(root);
}
return 0;
}
4.4 解析嵌套的 Json 對象和數(shù)組
對于 Json 字符串最復(fù)雜的個數(shù)莫過于 Json 對象和 Json 數(shù)組嵌套的形式,下面通過一個例子演示一下應(yīng)該如何解析,字符串格式如下:
{\"list\":[{\"name\":\"luffy\",\"age\":19},{\"name\":\"sabo\",\"age\":21}]}
在解析的時候,我們只需要按照從屬關(guān)系,一層層解析即可:
-
根節(jié)點是一個 Json 對象,基于根節(jié)點中的 key 值取出對應(yīng)的 value 值,得到一個 Json 數(shù)組
-
讀出 Json 數(shù)組的大小,遍歷里邊的各個元素,每個元素都是一個 Json 對象
-
將 Json 對象中的鍵值對根據(jù) key 值取出對應(yīng)的 value 值
-
從取出的 Value 值中讀出實際類型對應(yīng)的數(shù)值 示例代碼如下:
#include "cJSON.h"
#include
#include
int main(int argc, char **argv)
{
char *s = "{\"list\":[{\"name\":\"luffy\",\"age\":19},{\"name\":\"sabo\",\"age\":21}]}";
cJSON *root = cJSON_Parse(s);
if(!root)
{
printf("get root faild !\n");
return -1;
}
cJSON *list = cJSON_GetObjectItem(root, "list");
if(!list)
{
printf("no list!\n");
return -1;
}
int array_size = cJSON_GetArraySize(list);
printf("array size is %d\n",array_size);
for(int i=0; i< array_size; i )
{
cJSON* item = cJSON_GetArrayItem(list, i);
cJSON* name = cJSON_GetObjectItem(item, "name");
printf("name is %s\n",name->valuestring);
cJSON* age = cJSON_GetObjectItem(item, "age");
printf("age is %d\n",age->valueint);
}
if(root)
{
cJSON_Delete(root);
}
return 0;
}