說明:
本文章旨在總結備份、友善以後查詢,由于是個人總結,如有不對,歡迎指正;另外,内容大部分來自網絡、書籍、和各類手冊,如若侵權請告知,馬上删帖緻歉。
QQ 群 号:513683159 【互相學習】
内容來源:
CSDN-cJSON使用詳細教程 | 一個輕量級C語言JSON解析器
JSON的簡單介紹以及C語言的JSON庫使用
JSON的簡單介紹&cJSON庫使用(一)
cJSON資料結構
typedef struct cJSON
{
struct cJSON *next; //周遊數組或對象鍊的後向連結清單指針
struct cJSON *prev; //周遊數組或對象鍊的前向連結清單指針
struct cJSON *child; //數組或對象鍊的孩子節點
int type; //key的類型
char *valuestring; //字元串值
int valueint; //整數值
double valuedouble; //浮點數值
char *string; //key的名字
} cJSON;
從以上代碼可看出,cJSON設計思想是:連結清單。
資料指向:
next
指向下一節點,
prev
指向上一節點,
child
指向子節點(故:對象/數組是可以嵌套,指向新連結清單)
資料存儲:
string
表示key的名稱,
type
表示key的類型,
valuestring、valueint、valuedouble
則表示對應
type
類型的變量。
PS:
一個結點表示 數組的一個元素/對象的一個字段。
cJSON 類型
不管是數值類型、字元串類型或者對象類型等都使用該結構體,類型資訊通過辨別符 type來進行判斷,cJSON總共定義了7種類型:
#define cJSON_Invalid (0) //表示一個無效的不包含任何值的項。如果将項設定為所有零位元組,則自動具有此類型。
#define cJSON_False (1 << 0) //表示一個錯誤的布爾值。您還可以使用cJSON_IsBool檢查布爾值。
#define cJSON_True (1 << 1) //表示一個正确的布爾值。您還可以使用cJSON_IsBool檢查布爾值。
#define cJSON_NULL (1 << 2) //表示一個空值。
#define cJSON_Number (1 << 3) //表示一個數字值。該值在valuedouble和valueint中以double形式存儲。如果數值超出了整數的範圍,則valueint使用INT_MAX或INT_MIN。
#define cJSON_String (1 << 4) //表示一個字元串值。它以零終止字元串的形式存儲在valuestring中。
#define cJSON_Array (1 << 5) //表示一個數組值。這是通過将child指向表示數組中值的cJSON項連結清單來實作的。元素使用next和prev連結在一起,其中第一個元素是prev。next == NULL和最後一個元素next == NULL。
#define cJSON_Object (1 << 6) //表示一個對象值。對象的存儲方式與數組相同,唯一的差別是對象中的項将它們的鍵存儲為string。
#define cJSON_Raw (1 << 7) //表示任何類型的JSON,它存儲在valuestring中以零結尾的字元數組中。例如,可以使用它來避免反複列印相同的靜态JSON以節省性能。cJSON在解析時永遠不會建立這種類型。還要注意,cJSON不檢查它是否是有效的JSON。
另外還有以下兩個标志:
cJSON_IsReference:指定子元素所指向的項和/或valuestring不屬于這個項,它隻是一個引用。是以cJSON_Delete和其他函數将隻釋放這個項,而不是它的子元素/valuestring。
cJSON_StringIsConst:這意味着字元串指向常量字元串。這意味着cJSON_Delete和其他函數不會嘗試釋放字元串。
一、建構cJSON
對于每個值類型,都有一個
cJSON_Create xxx
可用于建立該類型項的函數。所有這些都将配置設定一個
cJSON
結構,稍後可以用
cJSON_Delete()
删除它。請注意,您必須在某些時候删除它們,否則将導緻記憶體洩漏。
重要提示:
如果你已經添加了一個項到一個數組或一個對象,你不能删除它與
cJSON_Delete
。将它添加到數組或對象中會轉移它的所有權,這樣當數組或對象被删除時,它也會被删除。您還可以使用
cJSON_SetValuestring()
來更改
cJSON_String()
的
valuestring
,而不需要手動釋放前面的
valuestring
。
(1)建立:
①建立任何類型的JSON (cJSON_CreateRaw)
②建立對象的JSON
1.建立:
cJSON_CreateObject()
建立一個空對象。
cJSON_CreateObjectReference()
建立一個不“擁有”其内容的對象,是以它的内容不會被cJSON_Delete删除。
2.添加項:
cJSON_AddItemToObject()
向對象添加項.
cJSON_AddItemToObjectCS()
向名稱為常量或引用(項目的
key
,
cJSON
結構中的字元串)的對象添加一個項目,這樣它就不會被
cJSON_Delete()
釋放。
cJSON_AddItemReferenceToArray()
,可添加一個元素作為對另一個對象、數組或字元串的引用。這意味着
cJSON_Delete()
不會删除該項目的子屬性或
valuestring
屬性,是以如果它們已經在其他地方使用,則不會發生雙重釋放。
3.取出項:
cJSON_DetachItemFromObjectCaseSensitive()
從一個對象中取出一個項,會傳回分離的項,是以一定要把它配置設定給一個指針,否則你會有一個記憶體洩漏。
4.删除項:
cJSON_DeleteItemFromObjectCaseSensitive()
删除項目.工作方式類似于
cJSON_DetachItemFromObjectCaseSensitive()
,然後是
cJSON_Delete()
。
cJSON_ReplaceItemInObjectCaseSensitive()
或
cJSON_ReplaceItemViaPointer()
在适當的位置替換對象中的項.若失敗,
cJSON_ReplaceItemViaPointer()
将傳回0。這在内部所做的是分離舊的項,删除它并在其位置插入新項。
5.擷取大小:
cJSON_GetArraySize()
獲得對象的大小.(這樣可以工作,因為在内部對象被存儲為數組。)
6.通路項:
cJSON_GetObjectItemCaseSensitive()
通路一個對象中的一個項
7.周遊:
要周遊一個對象,可以使用
cJSON_ArrayForEach
宏,方法與使用數組相同。
8.其他:
cJSON
還提供了友善的幫助函數,可以快速建立新項并将其添加到對象中,比如
cJSON_AddNullToObject()
。它們傳回一個指向新項的指針,如果失敗則傳回
NULL
。
③建立數組的JSON
1.建立:
cJSON_CreateArray()
建立一個空數組。
cJSON_CreateArrayReference()
建立一個不“擁有”其内容的數組,是以它的内容不會被
cJSON_Delete()
删除。
2.添加項:
cJSON_AddItemToArray()
将項添加到數組中,将項追加到末尾。
cJSON_AddItemReferenceToArray()
可将一個元素添加為另一個項、數組或字元串的引用。這意味着
cJSON_Delete
将不會删除那些項的子屬性或
valuestring
屬性,是以,如果它們已經在其他地方使用了,就不會發生重複釋放。
cJSON_InsertItemInArray()
,中間插入項,它将在給定的基于0的索引處插入一個項,并将所有現有項向右移動。
3.取出項:
cJSON_DetachItemFromArray()
,從一個給定索引的數組中取出一個項目并繼續使用它,它将傳回分離的項目,是以一定要将它配置設定給一個指針,否則您将有記憶體洩漏。
4.删除項:
cJSON_DeleteItemFromArray()
删除項目。它的工作原理類似于
cJSON_DetachItemFromArray()
,但是通過
cJSON_Delete
删除分離的項目。
5.替換項:
cJSON_ReplaceItemInArray()
用索引/
cJSON_ReplaceItemViaPointer()
給定元素指針,在适當的位置替換數組中的項.若
cJSON_ReplaceItemViaPointer()
失敗,它将傳回0。這在内部做的是分離舊項、删除它并在其位置插入新項。
6.擷取大小:
cJSON_GetArraySize()
獲得數組的大小。使用
cJSON_GetArrayItem()
擷取給定索引處的元素。
7.周遊:
因為數組存儲為一個連結清單,通過疊代索引效率低下(O (n²)),是以你可以使用
cJSON_ArrayForEach
宏周遊一個數組在O (n)時間複雜度。
④建立基礎類型的JSON
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
(2)删除
釋放cJSON
二、解析cJSON
①解析cJSON
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
cJSON_Parse()
:對給定以零結尾的字元串中的一些JSON進行解析。
cJSON_ParseWithLength()
:給定一個字元串中的一些JSON(無論是否終止為0)進行解析。
1.空間配置設定:
将解析JSON并配置設定一個表示它的cJSON項樹。一旦它傳回,将完全負責在與
cJSON_Delete()
一起使用後對它進行釋放。配置設定器預設是
malloc
和
free
,但是可以使用
cJSON_InitHooks()
(全局)更改。
2.發生錯誤:
cJSON_GetErrorPtr()
:通路指向輸入字元串中錯誤位置的指針。
注意:這可能會在多線程場景中産生競争條件,在這種情況下,最好使用
cJSON_ParseWithOpts()
和
return_parse_end
。預設情況下,解析後的JSON之後的輸入字元串中的字元不會被視為錯誤。
3.更多選項
若想要更多的選項,使用
cJSON_ParseWithOpts()
。
return_parse_end
傳回一個指針,指向輸入字元串中的JSON結尾或錯誤發生的位置(進而以線程安全的方式替換
cJSON_GetErrorPtr()
。
require_null_ended
,如果設定為1,那麼如果輸入字元串包含
JSON
之後的資料,則會導緻錯誤。
若你想要更多的設定緩沖區長度的選項,可以使用
cJSON_ParseWithLengthOpts()
②輸出JSON
cJSON_Print
()給定一個
cJSON
項樹,将它們列印為字元串(将使用空白來列印格式)
它将配置設定一個字元串并将樹的
JSON
表示形式列印到其中。一旦它傳回,您就完全有責任在與配置設定器一起使用後重新配置設定它。(通常是免費的,取決于
cJSON_InitHooks
設定了什麼)。
cJSON_PrintUnformatted ()
列印沒有格式,則可使用該函數。
cJSON_PrintBuffered ()
:若對結果字元串的大小有一個大緻的概念,則可使用該函數。
cJSON_PrintPreallocated()
避免這些動态緩沖區配置設定。它接受一個緩沖區的指針列印到它的長度。如果達到該長度,列印将失敗并傳回0。如果成功,則傳回1。注意,您應該提供比實際需要更多的5個位元組,因為cJSON在估計所提供的記憶體是否足夠時不是100%準确的。
③其他
1.從object中擷取item “string”(根據鍵找json結點)
2.判斷是否有key是string的項 如果有傳回1 否則傳回0
3 . 傳回數組(或對象)中的項數
4.從數組“array”中檢索項目編号“index”(根據數組下标index取array數組結點的第index個成員 傳回該成員節點)
5.周遊數組
實驗:
實驗一:建構json,并将其儲存到檔案中。
建立檔案:
creat_json.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "cJSON.h"
#define CFGFILE "creat_json.json"
//建構以下json
// {
// "name":"王小二", //字元串
// "age":18, //數值
// "favorite_food":["dumplings","ribs"], //數組
// "has_girlfriend":false, //布爾值
// "car":null, //null
// "address": //對象
// {
// "country":"China",
// "city":"fuzhou",
// "postcode":350000
// }
// }
char *buf = NULL;
void test()
{
cJSON *object = cJSON_CreateObject(); //建立一個空對象
cJSON *name = cJSON_CreateString("王小二"); //建立基礎類:string,并添加到對象中
cJSON_AddItemToObject(object, "name", name);
cJSON *age = cJSON_CreateNumber(18); //建立基礎類:number,并添加到對象中
cJSON_AddItemToObject(object, "age", age);
cJSON *array = cJSON_CreateArray(); //建立數組:array ,并添加到對象中
cJSON_AddItemToObject(object, "favorite_food", array);
cJSON *dumplings = cJSON_CreateString("dumplings"); //建立基礎類:string ,并添加到數組中
cJSON_AddItemToArray(array, dumplings);
cJSON *ribs = cJSON_CreateString("ribs"); //建立基礎類:string ,并添加到數組中
cJSON_AddItemToArray(array, ribs);
cJSON *has_girlfriend = cJSON_CreateFalse(); //建立基礎類:false,并添加到對象中
cJSON_AddItemToObject(object, "has_girlfriend", has_girlfriend);
cJSON *car = cJSON_CreateNull(); //建立基礎類:null,并添加到對象中
cJSON_AddItemToObject(object, "car", car);
cJSON *address =cJSON_CreateObject();
cJSON_AddItemToObject(object, "address", address);
cJSON *country = cJSON_CreateString("China"); //建立基礎類:string,并添加到對象中
cJSON_AddItemToObject(address, "country", country);
cJSON *city = cJSON_CreateString("fuzhou"); //建立基礎類:string,并添加到對象中
cJSON_AddItemToObject(address, "city", city);
cJSON *postcode = cJSON_CreateNumber(350000); //建立基礎類:string,并添加到對象中
cJSON_AddItemToObject(address, "postcode", postcode);
//将内容存到CFGFILE檔案中
FILE *fp = fopen(CFGFILE,"w");
buf = cJSON_Print(object);
fwrite(buf,strlen(buf),1,fp);
fclose(fp);
cJSON_Delete(object);
}
int main()
{
test();
printf("%s\n",buf); //顯示屏上檢視對應的資料
}
執行指令:
gcc creat_json.c -o creat -lcjson
或
gcc creat_json.c cJSON.c -I ./ -o creat
實驗二:解析檔案
檔案1:
conf_s_1.json
{
"name": "T1","type": "s1"
}
檔案2:
conf_s_2.json
{
"T": [
{"name": "T1","type": "s1"},
{"name": "T2","type": "s2"}
]
}
#include <stdlib.h>
#include <stdio.h>
#include "cJSON.h"
#define CFGFILE1 "conf_s_1.json"
#define CFGFILE2 "conf_s_2.json"
void test1(){
//将文本内容存到字元串data中
//打開檔案
FILE* file = fopen(CFGFILE1,"rb");
//獲得檔案長度
fseek(file,0,SEEK_END);
long len=ftell(file);
fseek(file,0,SEEK_SET);
//開辟記憶體,将檔案内容讀取到data中
char* data = (char*)malloc(len+1);
fread(data,1,len,file);
//關閉檔案
fclose(file);
cJSON* root = cJSON_Parse(data); //将cJSON實體呈現為用于傳輸/存儲的文本
cJSON* name_json = cJSON_GetObjectItem(root, "name"); //從對象中擷取項目“string”。不區分大小寫
char* name = cJSON_Print(name_json); //将cJSON實體呈現為用于傳輸/存儲的文本,而不進行任何格式化
printf("name:%s\n", name);
free(name);
cJSON* type_json = cJSON_GetObjectItem(root, "type"); //從對象中擷取項目“string”。不區分大小寫
char* type = cJSON_Print(type_json); //将cJSON實體呈現為用于傳輸/存儲的文本,而不進行任何格式化
printf("type:%s\n", type);
free(type);
cJSON_Delete(root); //删除一個cJSON實體和所有子實體
free(data); //删除記憶體
}
void test2(){
//将文本内容存到字元串data中
//打開檔案
FILE* file = fopen(CFGFILE2,"rb");
//獲得檔案長度
fseek(file,0,SEEK_END);
long len=ftell(file);
fseek(file,0,SEEK_SET);
//開辟記憶體,将檔案内容讀取到data中
char* data = (char*)malloc(len+1);
fread(data,1,len,file);
//關閉檔案
fclose(file);
cJSON* root = cJSON_Parse(data);
cJSON* OBJroot=cJSON_GetObjectItem(root, "T");
int array_size = cJSON_GetArraySize(OBJroot);
for (int i = 0; i < array_size; i++) {
printf("i:%d\n", i);
cJSON *object = cJSON_GetArrayItem(OBJroot, i);
cJSON *name_json = cJSON_GetObjectItem(object, "name");
char *name = cJSON_Print(name_json); //将JSON結構體列印到字元串中 需要自己釋放
printf("name:%s\n", name);
free(name);
cJSON *type_json = cJSON_GetObjectItem(object, "type");
char *type = cJSON_Print(type_json); //将JSON結構體列印到字元串中 需要自己釋放
printf("type:%s\n", type);
free(type);
}
cJSON_Delete(root);
free(data);
}
int main()
{
test1();
//test2();
}
實驗三:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"cJSON.h"
int main(void)
{
char *string = "{\"family\":[\"father\",\"mother\",\"brother\",\"sister\",\"somebody\"]}";
//從緩沖區中解析出JSON結構
cJSON *json = cJSON_Parse(string);
cJSON *node = NULL;
//cJOSN_GetObjectItem 根據key來查找json節點 若果有傳回非空
node = cJSON_GetObjectItem(json,"family");
if(node == NULL)
{
printf("family node == NULL\n");
}
else
{
printf("found family node\n");
}
node = cJSON_GetObjectItem(json,"famil");
if(node == NULL)
{
printf("famil node == NULL\n");
}
else
{
printf("found famil node\n");
}
//判斷是否有key是string的項 如果有傳回1 否則傳回0
if(1 == cJSON_HasObjectItem(json,"family"))
{
printf("found family node\n");
}
else
{
printf("not found family node\n");
}
if(1 == cJSON_HasObjectItem(json,"famil"))
{
printf("found famil node\n");
}
else
{
printf("not found famil node\n");
}
node = cJSON_GetObjectItem(json,"family");
if(node->type == cJSON_Array)
{
printf("array size is %d",cJSON_GetArraySize(node));
}
//非array類型的node 被當做array擷取size的大小是未定義的行為 不要使用
cJSON *tnode = NULL;
int size = cJSON_GetArraySize(node);
int i;
for(i=0;i<size;i++)
{
tnode = cJSON_GetArrayItem(node,i);
if(tnode->type == cJSON_String)
{
printf("value[%d]:%s\n",i,tnode->valuestring);
}
else
{
printf("node' type is not string\n");
}
}
cJSON_ArrayForEach(tnode,node)
{
if(tnode->type == cJSON_String)
{
printf("int forEach: vale:%s\n",tnode->valuestring);
}
else
{
printf("node's type is not string\n");
}
}
return 0;
}