當(dāng)前位置:首頁 > 公眾號精選 > C語言與CPP編程
[導(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/cJSON

cJSON,目前來說,主要的文件有兩個,一個 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)體做如下幾點的說明:

  1. cJOSN 結(jié)構(gòu)體是一個雙向鏈表,并且可通過 child 指針訪問下一層。
  2. 結(jié)構(gòu)體成員 type 變量用于描述數(shù)據(jù)元素的類型(如果是鍵值對表示 value 值的類型),數(shù)據(jù)元素可以是字符串可以是整形,也可以是浮點型。
  • 如果是整形值的話可通過 valueint 將值取出
  • 如果是浮點型的話可通過 valuedouble 將值取出
  • 如果是字符串類型的話可通過 valuestring 將值取出
  1. 結(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ù)組的同時還可以進(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)體)
為了讓我的操作更加方便,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ù),就需要對這個字符串進(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使用/

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉
關(guān)閉