天天看點

cjson學習篇三:cJSON資料結構和設計思想

說明:

  本文章旨在總結備份、友善以後查詢,由于是個人總結,如有不對,歡迎指正;另外,内容大部分來自網絡、書籍、和各類手冊,如若侵權請告知,馬上删帖緻歉。

  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;
}
           

繼續閱讀