cJSON是一個輕量級的json解析庫。使用起來很是簡單,整個庫很是地簡潔,核心功能的實現都在cJSON.c文件,很是適合閱讀源代碼來學習C語言。最近讀完這個庫的源碼,分享本身收穫的一些心得。git
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庫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的官網在這裏,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值。
若是遇到"開頭,則說明json值是字符串,就解析它的值,此時只須要拿到兩個"之間的值便可。保存字符串也是一個結構體,須要申請內存,計算長度的過程當中,當遇到轉義字符時,須要記錄,由於轉義符不保存。
當遇到數字開頭時,將其後面的數字字符記錄起來,而後轉成整型數字,而後作值的範圍檢查。
解析數組時,爲數組的元素建立一個新的json結構體new_item,而後繼續解析數組裏面的值,用','判斷下一個元素的位置,獲得的值保存到結構體中,並將多個元素用鏈表鏈接起來。一直解析,直到遇到']'符號。
解析對象的過程與數組的相似,爲對象的元素建立一個新的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的認識也有了一個更深入的印象。 學習到了一種解析某種格式的字符串的思路,要先知道該字符串格式的規範,直到它是如何組成的,有哪些規則和注意的地方,從它的組成規範中逐步分解和解析。