C/C ?關(guān)于?cJson?庫的使用
時間:2021-09-10 16:33:31
手機(jī)看文章
掃描二維碼
隨時隨地手機(jī)看文章
[導(dǎo)讀]關(guān)于Json這種數(shù)據(jù)格式,在前面已經(jīng)做了詳細(xì)的介紹?Json的格式和用途,在項目開發(fā)過程中我們需要針對不同的語言使用不同的庫對Json格式的數(shù)據(jù)進(jìn)行解析,下面給大家介紹一個基于C語言的Json庫–cJson。cJSON是一個超輕巧,攜帶方便,單文件,簡單的可以作為ANSI-C標(biāo)準(zhǔn)...
關(guān)于 Json 這種數(shù)據(jù)格式,在前面已經(jīng)做了詳細(xì)的介紹?Json 的格式和用途,在項目開發(fā)過程中我們需要針對不同的語言使用不同的庫對 Json 格式的數(shù)據(jù)進(jìn)行解析,下面給大家介紹一個基于 C 語言的 Json 庫 – cJson。cJSON 是一個超輕巧,攜帶方便,單文件,簡單的可以作為 ANSI-C 標(biāo)準(zhǔn)的 JSON 解析器。cJSON 是一個開源項目,github 下載地址:
https://github.com/DaveGamble/cJSONcJSON,目前來說,主要的文件有兩個,一個
cJSON.c
一個 cJSON.h
。使用的時候,將頭文件 include 進(jìn)去即可。如果是在 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 表示鍵值對中鍵值的名稱。
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ù)組的同時還可以進(jìn)行初始化//?創(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)系是通過樹來維護(hù)的,每一層節(jié)點都是通過鏈表來維護(hù)的,這樣就能分析出該函數(shù)參數(shù)的含義:- object:要添加的鍵值對從屬于那個節(jié)點
- string:添加的鍵值對的鍵值
- item:添加的鍵值對的 value 值(需要先將其封裝為 cJSON 類型的結(jié)構(gòu)體)
#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
表示格式化。
2.5 Json 字符串的解析
如果我們得到了一個 Json 格式的字符串,想要讀出里邊的數(shù)據(jù),就需要對這個字符串進(jìn)行解析,處理方式就是將字符串轉(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)一點:在進(jìn)行內(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)
進(jìn)行內(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án)格的場所,應(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????{
????????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????{
????????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;
}
文章鏈接:https://subingwen.cn/c/cjson使用/