【源碼分析】cJSON庫學習

cJSON庫是什麼?

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

什麼是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成爲理想的數據交換語言。github

cJSON庫裏面有什麼?

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

兩個函數的原型以下:數組

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使用兩種結構構建,對象或者數組。網站

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

每個值能夠是字符串,整型,也能夠是true,false,null等常量,還能夠是對象或數組,由於json結構是可嵌套的。spa

所以,咱們能夠得知:指針

一、能夠根據json的首字母判斷整個json的類型,若是json以'{'開頭時,就是對象,以'['開頭時,就是數組,不然就是字符串或者其餘常量。

二、若是是對象,那麼它的必定有鍵名,先解析它的鍵名,而後解析它的值,解析值的過程與第一步同樣,遞歸解析

三、若是是數組,則逐個解析數組內的元素,直到遇到]爲止,解析數組裏面的元素的過程也是與第一步一致,遞歸解析。

這就是根據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的認識也有了一個更深入的印象。 學習到了一種解析某種格式的字符串的思路,要先知道該字符串格式的規範,直到它是如何組成的,有哪些規則和注意的地方,從它的組成規範中逐步分解和解析。

相關文章
相關標籤/搜索