【源碼分析】cJSON庫學習


cJSON庫是什么?

cJSON是一個輕量級的json解析庫。使用起來非常簡單,整個庫非常地簡潔,核心功能的實現都在cJSON.c文件,非常適合閱讀源代碼來學習C語言。最近讀完這個庫的源碼,分享自己收獲的一些心得。

什么是json,照搬json官網的說法:

JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。 易於人閱讀和編寫。同時也易於機器解析和生成。 它基於JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集。 JSON采用完全獨立於語言的文本格式,但是也使用了類似於C語言家族的習慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 這些特性使JSON成為理想的數據交換語言。

cJSON庫里面有什么?

cjson庫github地址:https://github.com/DaveGamble/cJSON
整個庫包含cJSON.h和cJSON.c兩個文件,頭文件定義了一系列的API。這個庫最基本也最重要的功能就是解析一個json字符串,使用的API是cJSON_Parse。cJSON_Parse函數調用了cJSON_ParseWithOpts函數,該函數實現了具體的邏輯。

兩個函數的原型如下:

CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);

函數接收一段字符串,然后進行解析后返回。解析完返回的是一個cjson結構,cJSON結構的定義如下:

typedef struct cJSON
{
    struct cJSON *next; // 向后指針
    struct cJSON *prev; // 向前指針

    struct cJSON *child; // 指向子元素,比如子數組或者子對象

    int type; // 元素的類型

    char *valuestring; // 元素的字符串值,如果type == cJSON_String 或者 type == cJSO_Raw

    int valueint; // 已廢棄,現在使用cJSON_SetNumberValue設置整型值

    double valuedouble; // 元素的整型值,如果type == cJSON_Number

    char *string; // 表示元素鍵值的值,如果它有子元素的話
} cJSON;

如何解析一個json字符串?

json的官網在這里,http://www.json.org
網站首頁描述了json是什么以及它的格式規范,有了規范之后,可以知道json是如何構成的,因此就有了如何解析json數據的方向。

json使用兩種結構構建,對象或者數組。

對象使用{作開頭,}作結尾,里邊的每一個元素都是鍵值對的無序集合,鍵名和值使用:分隔,使用,分隔每一個元素;數組使用[作開頭,]作結尾,里面的元素都是有序的值組成的集合,且使用,做分隔符。

每一個值可以是字符串,整型,也可以是true,false,null等常量,還可以是對象或數組,因為json結構是可嵌套的。

因此,我們可以得知:

1、可以根據json的首字母判斷整個json的類型,如果json以'{'開頭時,就是對象,以'['開頭時,就是數組,否則就是字符串或者其他常量。

2、如果是對象,那么它的一定有鍵名,先解析它的鍵名,然后解析它的值,解析值的過程與第一步一樣,遞歸解析

3、如果是數組,則逐個解析數組內的元素,直到遇到]為止,解析數組里面的元素的過程也是與第一步一致,遞歸解析。

這就是根據json官網的定義得出解析json字符串的思路,接下來看看cJSON庫是如何實現的。cJSON_Parse的實現流程圖如下:

cJSON_ParseWithOpts函數里面調用了parse_value,是整個函數的核心實現。
parse_value函數的流程圖如下所示:

可以看到,parse_value是對json值的開頭進行判斷,然后進入相應的分支進行解析,下面對每一個分支進行分析。解析出來的值是保存在cJSON的結構體中,以下命名為item。

常量

如果json值是以'null','true','false',則分別將item的type設置為cJSON_NULL、cJSON_TRUE、cJSON_FALSE。然后繼續解析剩下的json值。

string

如果遇到"開頭,則說明json值是字符串,就解析它的值,此時只需要拿到兩個"之間的值即可。保存字符串也是一個結構體,需要申請內存,計算長度的過程中,當遇到轉義字符時,需要記錄,因為轉義符不保存。

number

當遇到數字開頭時,將其后面的數字字符記錄起來,然后轉成整型數字,然后做值的范圍檢查。

array

解析數組時,為數組的元素創建一個新的json結構體new_item,然后繼續解析數組里面的值,用','判斷下一個元素的位置,得到的值保存到結構體中,並將多個元素用鏈表連接起來。一直解析,直到遇到']'符號。

object

解析對象的過程與數組的類似,為對象的元素創建一個新的json結構體new_item,然后繼續解析對象里面的值,對象是有鍵值對組成的,因此先得到鍵的值,然后用':'判斷值的位置,進而繼續解析得到值,多個鍵值對之間用','分隔開,最后用鏈表連接起來。一直解析,直到遇到'}'符號。

其他

在解析所有值之前,會調用skip_whitespace函數過濾字符串兩邊的所有空白字符。此處是ASCII碼小於等於32的字符,如:\t、\n。函數如下:

static const unsigned char *skip_whitespace(const unsigned char *in)
{
    while (in && *in && (*in <= 32))
    {
        in++;
    }

    return in;
}

總結

通過閱讀這個小小的json解析庫,知道了大部分的json庫是如何實現的,自己對json的認識也有了一個更深刻的印象。
學習到了一種解析某種格式的字符串的思路,要先知道該字符串格式的規范,直到它是如何組成的,有哪些規則和注意的地方,從它的組成規范中逐步分解和解析。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM