關於cjson的介紹和使用方法就不在這里介紹了,詳情請查看上一篇博客cjson使用方法。
JSON的內存結構像廣義表,可以認為是有層次的雙向鏈表。
cJSON程序中的細節點如下:
- 大量宏替換
- 大量靜態函數
- 錯誤處理機制
- 字符串處理時存在utf16轉utf9,編碼轉換
- 用函數指針封裝malloc,free,方便用於處理,比如在申請后初始化,或者釋放前進行一些處理等。
cJSON中的重要接口函數如下:
解析函數 cJSON * cJSON_Parse(const char *value); 打印函數 char * cJSON_Print(cJSON * item); 刪除函數 void cJSON_Delete(cJSON * c); 構造函數 create系列和add系列 解析字符串 char *parse_string(cJSON*item,const char *str) 解析數字 char *parse_number(cJSON *item,const char *num) 解析數組 char *parse_array(cJSON *item,const char *value) 解析對象 char *parse_object(cJSON *item,const char *value) ......
cjosn有兩個相關的文件,一個cJSON.c和cJSON.h。我們先從頭文件開始分析。
首先,我們會看到頭文件的開頭和結尾這樣的語句:
- #ifndef cJSON__h
- #define cJSON__h
- #ifdef __cplusplus
- extern "C"
- {
- #endif
...
...
- #ifdef __cplusplus
- }
- #endif
- #endif
#ifndef cJSON_h,#define cJSON_h,#endif . 這是為了防止頭文件被重復引用。
extern "C"的主要作用就是為了能夠正確實現C++代碼調用其他C語言代碼。加上extern "C"后,會指示編譯器這部分代碼按C語言的進行編譯,而不是C++的。由於C++支持函數重載,因此編譯器編譯函數的過程中會將函數的參數類型也加到編譯后的代碼中,而不僅僅是函數名;而C語言並不支持函數重載,因此編譯C語言代碼的函數時不會帶上函數的參數類型,一般之包括函數名。
接着往下看,就會看到cjson的結構體的定義,cjson對象存儲結構實際是一個結構體。
- // JSON的一個value的結構體
- typedef struct cJSON
- {
- struct cJSON *next,*prev; // 同一級的元素使用雙向列表存儲
- struct cJSON *child; // 如果是一個object或array的話,child為第一個兒子的指針
- int type; // value的類型
- char *valuestring; // 如果這個value是字符串類型,則此處為字符串值
- int valueint; // 如果是數字的話,整數值
- double valuedouble; // 如果是數字的話,讀點數值
- char *string; // json對象的名稱
- } cJSON;
前面提到過,cjson的存儲結構像一個廣義表,其實也可以說是一個樹,不過兄弟結點之間都通過prev和next兩個指針連接起來。
prev和next分別是cjson對象的前驅和后繼,屬於同一級別的對象。chid則指向孩子結點,並且是第一個孩子的指針。
示例圖如下:
cjson的類型宏定義:
- /* cJSON Types: */
- #define cJSON_False (1 << 0)
- #define cJSON_True (1 << 1)
- #define cJSON_NULL (1 << 2)
- #define cJSON_Number (1 << 3)
- #define cJSON_String (1 << 4)
- #define cJSON_Array (1 << 5)
- #define cJSON_Object (1 << 6)
- #define cJSON_IsReference 256
- #define cJSON_StringIsConst 512
這些宏定義是對結構體type的值定義,處理時只需要將type的值&255進行位運算,即可得到json里儲存的數據類型。
cjson的創建:
cjson的創建的過程就是創建一個cjson結構體,再通過add一系列函數將其他孩子結點數據或同等級結點加入,將相關結點通過指針鏈起來。
cjson_create一系列函數:cJSON_CreateArray(),cJSON_CreateObject(),cJSON_CreateString()等函數,都是調用cJSON_New_Item()函數創建對應節點信息。函數返回一個json結構體指針。
相關函數如下:
- static cJSON *cJSON_New_Item(void) //創建json結構體
- {
- cJSON *node = (cJSON *) cJSON_malloc(sizeof(cJSON));
- if (node)
- memset(node, 0, sizeof(cJSON)); //初始化結構體
- return node;
- }
- cJSON *cJSON_CreateNull(void)
- {
- cJSON *item = cJSON_New_Item();
- if (item)
- item->type = cJSON_NULL;
- return item;
- }
- cJSON *cJSON_CreateTrue(void)
- {
- cJSON *item = cJSON_New_Item();
- if (item)
- item->type = cJSON_True;
- return item;
- }
- cJSON *cJSON_CreateFalse(void)
- {
- cJSON *item = cJSON_New_Item();
- if (item)
- item->type = cJSON_False;
- return item;
- }
- cJSON *cJSON_CreateBool(int b)
- {
- cJSON *item = cJSON_New_Item();
- if (item)
- item->type = b ? cJSON_True : cJSON_False;
- return item;
- }
- cJSON *cJSON_CreateNumber(double num)
- {
- cJSON *item = cJSON_New_Item();
- if (item) {
- item->type = cJSON_Number;
- item->valuedouble = num;
- item->valueint = (int) num;
- }
- return item;
- }
- cJSON *cJSON_CreateString(const char *string)
- {
- cJSON *item = cJSON_New_Item();
- if (item) {
- item->type = cJSON_String;
- item->valuestring = cJSON_strdup(string);
- }
- return item;
- }
- cJSON *cJSON_CreateArray(void)
- {
- cJSON *item = cJSON_New_Item();
- if (item)
- item->type = cJSON_Array;
- return item;
- }
- cJSON *cJSON_CreateObject(void)
- {
- cJSON *item = cJSON_New_Item();
- if (item)
- item->type = cJSON_Object;
- return item;
- }
創建完一個根結點結構體后,接下來就是向根結點中加入元素。
從頭文件我們發現,
- // 創建一個string值為name的cJSON_Null節點,並添加到object
- #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
- // 創建一個string值為name的cJSON_True節點,並添加到object
- #define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
- // 創建一個string值為name的cJSON_False節點,並添加到object
- #define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
- // 創建一個string值為name的cJSON_CreateBool節點,並添加到object。b非0為cJSON_True,0為cJSON_False。
- #define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
- // 創建一個string值為name,valuedouble為n,valueint為(int)n的cJSON_Number節點,並添加到object。
- #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
- // 創建一個string值為name,valuestring為s的cJSON_String節點,並添加到object。
- #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
過程是調用cJSON_AddItemToObject()並結合不同的對象類型增加節點名稱和子節點。然后在其中調用cJSON_AddItemToArray()函數來添加信息,此函數中判斷對象孩子結點是否為NULL,如果是NULL,則直接插入,否則找到最后一個孩子,調用suffix_object()函數添加到雙向鏈表的尾部。
示例圖如下:
相關代碼如下:
- // 將字符串添加進對象
- void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item)
- {
- if (!item)
- return;
- if (item->string)
- cJSON_free(item->string); // 這個兒子之前有key,先清理
- item->string=cJSON_strdup(string); // 設置key值
- cJSON_AddItemToArray(object,item); // 添加兒子
- }
- // 將傳入的字符串復制一副本並返回新的字符串指針
- static char* cJSON_strdup(const char* str)
- {
- size_t len;
- char* copy;
- len = strlen(str) + 1;
- // 分配空間
- if (!(copy = (char*)cJSON_malloc(len)))
- return 0;
- // 執行復制操作
- memcpy(copy,str,len);
- // 返回復制的副本
- return copy;
- }
- // 添加節點到object或array中
- void cJSON_AddItemToArray(cJSON *array, cJSON *item)
- {
- cJSON *c=array->child;
- if (!item)
- return;
- if (!c)
- {
- array->child=item; // 之前不存在兒子節點,直接添加
- }
- else
- {
- while (c && c->next) // 先找到最后一個兒子
- c=c->next;
- suffix_object(c,item); // 添加兒子,c是item的兄弟節點
- }
- }
- // array的處理
- static void suffix_object(cJSON *prev,cJSON *item)
- {
- // 兩個兄弟的指針互相指向對方
- prev->next=item;
- item->prev=prev;
- }
cjson打印:
cjson打印就是從根對象的結構體開始遍歷,得到每個item結點的名稱和數據,並經過處理成特定的cjson字符串的輸出形式。
cJSON_Print(root)和cJSON_PrintUnformatted(root) 函數都是打印成json字符串的函數,兩者的區別就是
cJSON_PrintUnformatted(root) 處理成的字符串里沒有\t\n這類的格式,我們在這里以分析cJSON_Print(root)函數
為例,進行分析。
相關函數結構圖如下:
相關函數如下:
- typedef struct {
- char *buffer;
- int length;
- int offset;
- } printbuffer;
- static int pow2gt(int x) /*返回 一個比x的n(其中n是2的冪),並且是最小的冪,說白了就是將一個數后邊所有的位都置1然后再+1*/
- {
- --x;
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- return x + 1;
- }
- /* ensure 函數 是一個 協助 printbuffer 分配內存的一個函數
- * len 表示當前字符串的字符串起始偏移量 即 newbuffer+p->offset 起始的
- */
- static char* ensure(printbuffer *p,int needed)
- {
- char *newbuffer;int newsize;
- if (!p || !p->buffer) return 0;//傳入參數合法性檢測
- needed+=p->offset;//需要額外分配的內存 也就是偏移量
- if (needed<=p->length) return p->buffer+p->offset;//內存夠用直接返回
- newsize=pow2gt(needed);
- newbuffer=(char*)cJSON_malloc(newsize);//malloc出新內存 放buffer里面的內容
- if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;}
- if (newbuffer) memcpy(newbuffer,p->buffer,p->length);//
- cJSON_free(p->buffer);//
- p->length=newsize;
- p->buffer=newbuffer;
- return newbuffer+p->offset;//
- }
- char *cJSON_Print(cJSON * item)
- {
- return print_value(item, 0, 1, 0);
- }
- static char *print_value(cJSON * item, int depth, int fmt, printbuffer * p)
- {
- char *out = 0;
- if (!item)
- return 0;
- if (p) {
- switch ((item->type) & 255) {
- case cJSON_NULL:{
- out = ensure(p, 5);
- if (out)
- strcpy(out, "null");
- break;
- }
- case cJSON_False:{
- out = ensure(p, 6);
- if (out)
- strcpy(out, "false");
- break;
- }
- case cJSON_True:{
- out = ensure(p, 5);
- if (out)
- strcpy(out, "true");
- break;
- }
- case cJSON_Number:
- out = print_number(item, p); //打印數字函數
- break;
- case cJSON_String:
- out = print_string(item, p); //打印字符串函數
- break;
- case cJSON_Array:
- out = print_array(item, depth, fmt, p); //打印數組函數
- break;
- case cJSON_Object:
- out = print_object(item, depth, fmt, p); //打印object對象類型的函數
- break;
- }
- } else {
- switch ((item->type) & 255) {
- case cJSON_NULL:
- out = cJSON_strdup("null");
- break;
- case cJSON_False:
- out = cJSON_strdup("false");
- break;
- case cJSON_True:
- out = cJSON_strdup("true");
- break;
- case cJSON_Number:
- out = print_number(item, 0);
- break;
- case cJSON_String:
- out = print_string(item, 0);
- break;
- case cJSON_Array:
- out = print_array(item, depth, fmt, 0);
- break;
- case cJSON_Object:
- out = print_object(item, depth, fmt, 0);
- break;
- }
- }
- return out;
- }
- static char *print_number(cJSON * item, printbuffer * p) //打印數字函數
- {
- char *str = 0;
- double d = item->valuedouble;
- if (d == 0) {
- if (p)
- str = ensure(p, 2);
- else
- str = (char *) cJSON_malloc(2); /* special case for 0. */
- if (str)
- strcpy(str, "0");
- } else if (fabs(((double) item->valueint) - d) <= DBL_EPSILON
- && d <= INT_MAX && d >= INT_MIN) {
- if (p)
- str = ensure(p, 21);
- else
- str = (char *) cJSON_malloc(21); /* 2 ^ 64 + 1可以用21個字符表示 */
- if (str)
- sprintf(str, "%d", item->valueint);
- } else {
- if (p)
- str = ensure(p, 64);
- else
- str = (char *) cJSON_malloc(64); /* This is a nice tradeoff. */
- if (str) {
- if (fpclassify(d) != FP_ZERO && !isnormal(d)) //非正常浮點數
- sprintf(str, "null");
- else if (fabs(floor(d) - d) <= DBL_EPSILON
- && fabs(d) < 1.0e60)
- sprintf(str, "%.0f", d);
- else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9)
- sprintf(str, "%e", d);
- else
- sprintf(str, "%f", d);
- }
- }
- return str;
- }
- static char *print_string(cJSON * item, printbuffer * p) //打印字符串類型的結點
- {
- return print_string_ptr(item->valuestring, p);
- }
- static char *print_string_ptr(const char *str, printbuffer * p) //打印字符串類型的結點
- {
- const char *ptr;
- char *ptr2, *out;
- int len = 0, flag = 0;
- unsigned char token;
- if (!str) {
- if (p)
- out = ensure(p, 3);
- else
- out = (char *) cJSON_malloc(3);
- if (!out)
- return 0;
- strcpy(out, "\"\""); //字符串為空
- return out;
- }
- for (ptr = str; *ptr; ptr++)
- flag |= ((*ptr > 0 && *ptr < 32) || (*ptr == '\"')
- || (*ptr == '\\')) ? 1 : 0;
- if (!flag) { //對字符串中不含'\','/',空格等字符的字符處理
- len = ptr - str;
- if (p)
- out = ensure(p, len + 3);
- else
- out = (char *) cJSON_malloc(len + 3);
- if (!out)
- return 0;
- ptr2 = out;
- *ptr2++ = '\"';
- strcpy(ptr2, str);
- ptr2[len] = '\"';
- ptr2[len + 1] = 0;
- return out;
- }
- ptr = str;
- while ((token = *ptr) && ++len) {
- if (strchr("\"\\\b\f\n\r\t", token))
- len++;
- else if (token < 32)
- len += 5;
- ptr++;
- }
- if (p)
- out = ensure(p, len + 3);
- else
- out = (char *) cJSON_malloc(len + 3);
- if (!out)
- return 0;
- ptr2 = out;
- ptr = str;
- *ptr2++ = '\"';
- while (*ptr) {
- if ((unsigned char) *ptr > 31 && *ptr != '\"'
- && *ptr != '\\')
- *ptr2++ = *ptr++;
- else {
- *ptr2++ = '\\';
- switch (token = *ptr++) {
- case '\\':
- *ptr2++ = '\\';
- break;
- case '\"':
- *ptr2++ = '\"';
- break;
- case '\b':
- *ptr2++ = 'b';
- break;
- case '\f':
- *ptr2++ = 'f';
- break;
- case '\n':
- *ptr2++ = 'n';
- break;
- case '\r':
- *ptr2++ = 'r';
- break;
- case '\t':
- *ptr2++ = 't';
- break;
- default:
- sprintf(ptr2, "u%04x", token);
- ptr2 += 5;
- break; /* escape and print */
- }
- }
- }
- *ptr2++ = '\"';
- *ptr2++ = 0;
- return out;
- }
- static char *print_array(cJSON * item, int depth, int fmt, printbuffer * p) //打印數組類型結點函數
- {
- char **entries;
- char *out = 0, *ptr, *ret;
- int len = 5;
- cJSON *child = item->child;
- int numentries = 0, i = 0, fail = 0;
- size_t tmplen = 0;
- /* 數組里有多少個元素 */
- while (child)
- numentries++, child = child->next;
- /* 明確處理numentries = = 0 */ //處理空數組
- if (!numentries) {
- if (p)
- out = ensure(p, 3);
- else
- out = (char *) cJSON_malloc(3);
- if (out)
- strcpy(out, "[]");
- return out;
- }
- if (p) {
- /* 組成數組的輸出形式 */
- i = p->offset;
- ptr = ensure(p, 1);
- if (!ptr)
- return 0;
- *ptr = '[';
- p->offset++;
- child = item->child;
- while (child && !fail) {
- print_value(child, depth + 1, fmt, p);
- p->offset = update(p);
- if (child->next) {
- len = fmt ? 2 : 1;
- ptr = ensure(p, len + 1);
- if (!ptr)
- return 0;
- *ptr++ = ',';
- if (fmt)
- *ptr++ = ' ';
- *ptr = 0;
- p->offset += len;
- }
- child = child->next;
- }
- ptr = ensure(p, 2);
- if (!ptr)
- return 0;
- *ptr++ = ']';
- *ptr = 0;
- out = (p->buffer) + i;
- } else {
- /* 分配一個指針數組存儲數組里的每一個元素的打印結果 */
- entries =
- (char **) cJSON_malloc(numentries * sizeof(char *));
- if (!entries)
- return 0;
- memset(entries, 0, numentries * sizeof(char *));
- /* 檢索所有結果: */
- child = item->child;
- while (child && !fail) {
- ret = print_value(child, depth + 1, fmt, 0);
- entries[i++] = ret;
- if (ret)
- len += strlen(ret) + 2 + (fmt ? 1 : 0);
- else
- fail = 1;
- child = child->next;
- }
- if (!fail)
- out = (char *) cJSON_malloc(len);
- if (!out)
- fail = 1;
- if (fail) {
- for (i = 0; i < numentries; i++)
- if (entries[i])
- cJSON_free(entries[i]);
- cJSON_free(entries);
- return 0;
- }
- /* 組成數組的輸出形式. */
- *out = '[';
- ptr = out + 1;
- *ptr = 0;
- for (i = 0; i < numentries; i++) {
- tmplen = strlen(entries[i]);
- memcpy(ptr, entries[i], tmplen);
- ptr += tmplen;
- if (i != numentries - 1) {
- *ptr++ = ',';
- if (fmt)
- *ptr++ = ' ';
- *ptr = 0;
- }
- cJSON_free(entries[i]);
- }
- cJSON_free(entries);
- *ptr++ = ']';
- *ptr++ = 0;
- }
- return out;
- }
- /* 打印object類型結點. */
- static char *print_object(cJSON * item, int depth, int fmt, printbuffer * p)
- {
- char **entries = 0, **names = 0;
- char *out = 0, *ptr, *ret, *str;
- int len = 7, i = 0, j;
- cJSON *child = item->child;
- int numentries = 0, fail = 0;
- size_t tmplen = 0;
- /* 統計有多少個子結點. */
- while (child)
- numentries++, child = child->next;
- /* 明確處理空對象的情況*/
- if (!numentries) {
- if (p)
- out = ensure(p, fmt ? depth + 4 : 3);
- else
- out = (char *) cJSON_malloc(fmt ? depth + 4 : 3);
- if (!out)
- return 0;
- ptr = out;
- *ptr++ = '{';
- if (fmt) {
- *ptr++ = '\n';
- for (i = 0; i < depth; i++)
- *ptr++ = '\t';
- }
- *ptr++ = '}';
- *ptr++ = 0;
- return out;
- }
- if (p) {
- /* 組成輸出形式: */
- i = p->offset;
- len = fmt ? 2 : 1;
- ptr = ensure(p, len + 1);
- if (!ptr)
- return 0;
- *ptr++ = '{';
- if (fmt)
- *ptr++ = '\n';
- *ptr = 0;
- p->offset += len;
- child = item->child;
- depth++;
- while (child) {
- if (fmt) {
- ptr = ensure(p, depth);
- if (!ptr)
- return 0;
- for (j = 0; j < depth; j++)
- *ptr++ = '\t';
- p->offset += depth;
- }
- print_string_ptr(child->string, p);
- p->offset = update(p);
- len = fmt ? 2 : 1;
- ptr = ensure(p, len);
- if (!ptr)
- return 0;
- *ptr++ = ':';
- if (fmt)
- *ptr++ = '\t';
- p->offset += len;
- print_value(child, depth, fmt, p);
- p->offset = update(p);
- len = (fmt ? 1 : 0) + (child->next ? 1 : 0);
- ptr = ensure(p, len + 1);
- if (!ptr)
- return 0;
- if (child->next)
- *ptr++ = ',';
- if (fmt)
- *ptr++ = '\n';
- *ptr = 0;
- p->offset += len;
- child = child->next;
- }
- ptr = ensure(p, fmt ? (depth + 1) : 2);
- if (!ptr)
- return 0;
- if (fmt)
- for (i = 0; i < depth - 1; i++)
- *ptr++ = '\t';
- *ptr++ = '}';
- *ptr = 0;
- out = (p->buffer) + i;
- } else {
- /*為對象和名稱分配空間 */
- entries =
- (char **) cJSON_malloc(numentries * sizeof(char *));
- if (!entries)
- return 0;
- names =
- (char **) cJSON_malloc(numentries * sizeof(char *));
- if (!names) {
- cJSON_free(entries);
- return 0;
- }
- memset(entries, 0, sizeof(char *) * numentries);
- memset(names, 0, sizeof(char *) * numentries);
- /* 將所有結果收集到數組: */
- child = item->child;
- depth++;
- if (fmt)
- len += depth;
- while (child && !fail) {
- names[i] = str =
- print_string_ptr(child->string, 0);
- entries[i++] = ret =
- print_value(child, depth, fmt, 0);
- if (str && ret)
- len +=
- strlen(ret) + strlen(str) + 2 +
- (fmt ? 2 + depth : 0);
- else
- fail = 1;
- child = child->next;
- }
- if (!fail)
- out = (char *) cJSON_malloc(len);
- if (!out)
- fail = 1;
- if (fail) {
- for (i = 0; i < numentries; i++) {
- if (names[i])
- cJSON_free(names[i]);
- if (entries[i])
- cJSON_free(entries[i]);
- }
- cJSON_free(names);
- cJSON_free(entries);
- return 0;
- }
- /* 組成輸出形式: */
- *out = '{';
- ptr = out + 1;
- if (fmt)
- *ptr++ = '\n';
- *ptr = 0;
- for (i = 0; i < numentries; i++) {
- if (fmt)
- for (j = 0; j < depth; j++)
- *ptr++ = '\t';
- tmplen = strlen(names[i]);
- memcpy(ptr, names[i], tmplen);
- ptr += tmplen;
- *ptr++ = ':';
- if (fmt)
- *ptr++ = '\t';
- strcpy(ptr, entries[i]);
- ptr += strlen(entries[i]);
- if (i != numentries - 1)
- *ptr++ = ',';
- if (fmt)
- *ptr++ = '\n';
- *ptr = 0;
- cJSON_free(names[i]);
- cJSON_free(entries[i]);
- }
- cJSON_free(names);
- cJSON_free(entries);
- if (fmt)
- for (i = 0; i < depth - 1; i++)
- *ptr++ = '\t';
- *ptr++ = '}';
- *ptr++ = 0;
- }
- return out;
- }
cJSON解析:
首先,調用cJSON_Parse()函數,此函數是一個二次封裝函數,其內部為cJSON_ParseWithOpts()函數,該函數用於提取更多的解析選項,如果需要,最后返回解析結束的位置。而在上面的函數中,調用parse_value()函數進行解析,而該函數首先創建cJSON_NewItem()創建節點,用於存放解析的JSON結構數據,然后根據不同的選項,調用解析函數,其為parse_string(),parse_number(),parse_array(),parse_objec()等。
結構圖如下:
相關函數如下:
- // cJSON解析的二次封裝函數
- cJSON *cJSON_Parse(const char *value)
- {
- return cJSON_ParseWithOpts(value,0,0);
- }
- // 解析對象,創建一個新的根並初始化,返回一個cJSON類型
- cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
- {
- const char *end=0;
- cJSON *c=cJSON_New_Item(); //創建一個新結點
- ep=0; //ep為全局變量,錯誤信息就保存在全局字符串指針ep里,調用cJSON_GetErrorPtr()可以得要錯誤原因的描述字符串
- if (!c)
- return 0;
- end=parse_value(c,skip(value));
- if (!end)
- {
- cJSON_Delete(c);
- return 0;
- } /* parse failure. ep is set. */
- /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
- if (require_null_terminated)
- {
- end=skip(end);
- if (*end)
- {
- cJSON_Delete(c);
- ep=end;
- return 0;
- }
- }
- if (return_parse_end)
- *return_parse_end=end;
- return c;
- }
- // 解析器核心函數
- static const char *parse_value(cJSON *item,const char *value)
- {
- if (!value)
- return 0; /* Fail on null. */
- if (!strncmp(value,"null",4))
- {
- item->type=cJSON_NULL;
- return value+4;
- }
- if (!strncmp(value,"false",5))
- {
- item->type=cJSON_False;
- return value+5;
- }
- if (!strncmp(value,"true",4))
- {
- item->type=cJSON_True;
- item->valueint=1;
- return value+4;
- }
- if (*value=='\"')
- {
- return parse_string(item,value);
- }
- if (*value=='-' || (*value>='0' && *value<='9'))
- {
- return parse_number(item,value);
- }
- if (*value=='[')
- {
- return parse_array(item,value);
- }
- if (*value=='{')
- {
- return parse_object(item,value);
- }
- ep=value;
- return 0; /* failure. */
- }
- static const char *parse_string(cJSON *item,const char *str) //解析字符串函數
- {
- const char *ptr=str+1;
- char *ptr2;
- char *out;
- int len=0;
- unsigned uc,uc2;
- if (*str!='\"') // 不是字符串情況
- {
- ep=str;
- return 0;
- } /* not a string! */
- while (*ptr!='\"' && *ptr && ++len)
- if (*ptr++ == '\\')
- ptr++; // 跳出前面的引用
- out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
- if (!out)
- return 0;
- ptr=str+1;
- ptr2=out;
- while (*ptr!='\"' && *ptr)
- {
- if (*ptr!='\\')
- *ptr2++=*ptr++;
- else
- {
- ptr++;
- switch (*ptr)
- {
- case 'b': *ptr2++='\b'; break;
- case 'f': *ptr2++='\f'; break;
- case 'n': *ptr2++='\n'; break;
- case 'r': *ptr2++='\r'; break;
- case 't': *ptr2++='\t'; break;
- case 'u': /* transcode utf16 to utf8. */
- uc=parse_hex4(ptr+1);
- ptr+=4; /* get the unicode char. */
- if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0)
- break; /* check for invalid. */
- if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
- {
- if (ptr[1]!='\\' || ptr[2]!='u')
- break; /* missing second-half of surrogate. */
- uc2=parse_hex4(ptr+3);ptr+=6;
- if (uc2<0xDC00 || uc2>0xDFFF)
- break; /* invalid second-half of surrogate. */
- uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
- }
- len=4;
- if (uc<0x80)
- len=1;
- else if (uc<0x800)
- len=2;
- else if (uc<0x10000)
- len=3;
- ptr2+=len;
- switch (len)
- {
- case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
- case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
- case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
- case 1: *--ptr2 =(uc | firstByteMark[len]);
- }
- ptr2+=len;
- break;
- default: *ptr2++=*ptr; break;
- }
- ptr++;
- }
- }
- *ptr2=0;
- if (*ptr=='\"') ptr++;
- item->valuestring=out;
- item->type=cJSON_String;
- return ptr;
- }
- // 跳過這些空格,這里跳過了ascii值小於32的。
- static const char *skip(const char *in)
- {
- while (in && *in && (unsigned char)*in<=32)
- in++;
- return in;
- }
- // parse_number函數功能:解析數字,對輸入的文本生成一個數字,並填充結果項,傳入參數有兩
- // 個,這里先只關注num,返回值是一個字符串
- static const char *parse_number(cJSON *item,const char *num)
- {
- double n=0,sign=1,scale=0;
- int subscale=0,signsubscale=1;
- if (*num=='-') sign=-1,num++; // 判斷數字是否是有符號數字
- if (*num=='0') num++; // 判斷數字是否為0
- if (*num>='1' && *num<='9')
- do // 轉換數字
- n=(n*10.0)+(*num++ -'0');
- while (*num>='0' && *num<='9');
- if (*num=='.' && num[1]>='0' && num[1]<='9') // 對小數點后邊的部分進行處理,scale記錄小數點后邊的位數
- {
- num++;
- do
- n=(n*10.0)+(*num++ -'0'),scale--; // scale為小數點后的位數
- while (*num>='0' && *num<='9');
- }
- if (*num=='e' || *num=='E') // 是否為指數,科學計數法
- {
- num++;
- if (*num=='+') // 判斷指數后邊冪的正負號
- num++;
- else if (*num=='-')
- signsubscale=-1,num++;
- while (*num>='0' && *num<='9') // 處理指數后邊10的冪
- subscale=(subscale*10)+(*num++ - '0');
- }
- // 將字符串轉換為相應的數值
- n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
- item->valuedouble=n; // 將算出來的值存入緩存
- item->valueint=(int)n; // 將算出來的值存入緩存
- item->type=cJSON_Number; // 目標類型為數字
- return num;
- }
- // 從輸入文本中構建array
- static const char *parse_array(cJSON *item,const char *value)
- {
- cJSON *child;
- if (*value!='[') {ep=value;return 0;} /* not an array! */
- item->type=cJSON_Array;
- value=skip(value+1);
- if (*value==']') return value+1; /* empty array. */
- item->child=child=cJSON_New_Item();
- if (!item->child) return 0; /* memory fail */
- value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
- if (!value) return 0;
- while (*value==',')
- {
- cJSON *new_item;
- if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
- child->next=new_item;new_item->prev=child;child=new_item;
- value=skip(parse_value(child,skip(value+1)));
- if (!value) return 0; /* memory fail */
- }
- if (*value==']') return value+1; /* end of array */
- ep=value;return 0; /* malformed. */
- }
- // 從輸入文本中構建object
- static const char *parse_object(cJSON *item,const char *value)
- {
- cJSON *child;
- if (*value!='{') {ep=value;return 0;} /* not an object! */
- item->type=cJSON_Object;
- value=skip(value+1);
- if (*value=='}') return value+1; /* empty array. */
- item->child=child=cJSON_New_Item();
- if (!item->child) return 0;
- value=skip(parse_string(child,skip(value)));
- if (!value) return 0;
- child->string=child->valuestring;child->valuestring=0;
- if (*value!=':') {ep=value;return 0;} /* fail! */
- value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
- if (!value) return 0;
- while (*value==',')
- {
- cJSON *new_item;
- if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
- child->next=new_item;new_item->prev=child;child=new_item;
- value=skip(parse_string(child,skip(value+1)));
- if (!value) return 0;
- child->string=child->valuestring;child->valuestring=0;
- if (*value!=':') {ep=value;return 0;} /* fail! */
- value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
- if (!value) return 0;
- }
- if (*value=='}') return value+1; /* end of array */
- ep=value;return 0; /* malformed. */
- }
- // 將十六進制的字符串轉換為數字表示!
- static unsigned parse_hex4(const char *str)
- {
- unsigned h=0;
- if (*str>='0' && *str<='9')
- h+=(*str)-'0';
- else if (*str>='A' && *str<='F')
- h+=10+(*str)-'A';
- else if (*str>='a' && *str<='f')
- h+=10+(*str)-'a';
- else
- return 0;
- h=h<<4;str++;
- if (*str>='0' && *str<='9')
- h+=(*str)-'0';
- else if (*str>='A' && *str<='F')
- h+=10+(*str)-'A';
- else if (*str>='a' && *str<='f')
- h+=10+(*str)-'a';
- else
- return 0;
- h=h<<4;str++;
- if (*str>='0' && *str<='9')
- h+=(*str)-'0';
- else if (*str>='A' && *str<='F')
- h+=10+(*str)-'A';
- else if (*str>='a' && *str<='f')
- h+=10+(*str)-'a';
- else
- return 0;
- h=h<<4;str++;
- if (*str>='0' && *str<='9')
- h+=(*str)-'0';
- else if (*str>='A' && *str<='F')
- h+=10+(*str)-'A';
- else if (*str>='a' && *str<='f')
- h+=10+(*str)-'a';
- else
- return 0;
- return h;
- }
cJSON內存管理:
hook管理函數:
在 c 語言中內存一般是 malloc 和 free 的。
為了方便用戶自由的管理內存, cjson 使用 Hook 技術來讓使用者可以自定義內存管理函數。
即用戶自定義 malloc 和 free.
具體實現方式可以參考下面的代碼, 默認使用系統的 malloc 和 free 函數, 用過 cJSON_InitHooks 函數可以替換成用戶自定義的 malloc 和 free 函數。
- typedef struct cJSON_Hooks
- {
- void *(*malloc_fn)(size_t sz);
- void (*free_fn)(void *ptr);
- } cJSON_Hooks;
- // 對cJSON提供的分配,再分配,釋放內存初始化函數
- extern void cJSON_InitHooks(cJSON_Hooks* hooks);
- // 默認將分配和釋放空間函數指針指向malloc和free
- static void *(*cJSON_malloc)(size_t sz) = malloc;
- static void (*cJSON_free)(void *ptr) = free;
- // 其使用Hook技術來讓使用者可以自定義內存管理函數。其中默認系統使用的內存分配和釋放函數是malloc
- // 和free函數,利用cJSON_InitHooks函數可以替換成用戶自定義的malloc和free函數。
- void cJSON_InitHooks(cJSON_Hooks* hooks)
- {
- // 如果未定義,則使用默認的malloc和free函數
- if (!hooks) { /* Reset hooks */
- cJSON_malloc = malloc;
- cJSON_free = free;
- return;
- }
- // 定義了,則使用用戶自定義的malloc和free函數
- cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
- cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
- }
cJSON的刪除:
刪除節點很簡單, 先刪除兒子,然后清理內存即可。
總結一下就是對於 object 和 array 需要先刪除兒子,然后刪除自己。
對於 字符串, 需要先釋放字符串的內存, 再釋放自己這塊內存。
對於其他節點,直接釋放自己這塊內存。
- /*刪除結點函數*/
- void cJSON_Delete(cJSON *c) {
- cJSON *next;
- while (c) {
- next=c->next;
- if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
- if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
- if (c->string) cJSON_free(c->string);
- cJSON_free(c);
- c=next;
- }
- }
- /*刪除兒子結點函數*/
- 刪除也是從 array 和 object 中刪除,實現就比較簡潔了。
- void cJSON_DeleteItemFromArray(cJSON *array,int which) {
- cJSON_Delete(cJSON_DetachItemFromArray(array,which));
- }
- void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {
- cJSON_Delete(cJSON_DetachItemFromObject(object,string));
- }
Detach 又是什么東西呢?
我們把一個節點從 json 樹中刪除, 但是不釋放內存,而是先保留這個節點的指針, 這樣儲存在這個節點的信息都保留了下來。
接下來我們就可以做很多事了, 合適的時候添加到其他對象中, 合適的時候釋放內存。
比如上面的 delete 函數, 就需要真實的刪除了, 這個時候我們刪除即可。
而 detach 實現也比較簡單, 只是少了一步刪除操作。
- // 節點從雙向鏈表中刪除即可
- cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {
- cJSON *c=array->child;
- while (c && which>0) c=c->next,which--;
- if (!c) return 0;
- if (c->prev) c->prev->next=c->next;
- if (c->next) c->next->prev=c->prev;
- if (c==array->child) array->child=c->next;
- c->prev=c->next=0;
- return c;
- }
- cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {
- int i=0;
- cJSON *c=object->child;
- while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;
- if (c) return cJSON_DetachItemFromArray(object,i);
- return 0;
- }
其他函數:
前面我們已經將json功能分為三大塊進行了解析,現在把剩余的一些函數貼上,這些函數單獨分析即可。
- // 返回節點的個數
- int cJSON_GetArraySize(cJSON *array)
- {
- cJSON *c=array->child;
- int i=0;
- while(c)
- i++,c=c->next;
- return i;
- }
- // 返回array中第item個節點的地址
- cJSON *cJSON_GetArrayItem(cJSON *array,int item)
- {
- cJSON *c=array->child;
- while (c && item>0)
- item--,c=c->next;
- return c;
- }
- // 返回Object中第item個節點的地址
- cJSON *cJSON_GetObjectItem(cJSON *object,const char *string)
- {
- cJSON *c=object->child;
- while (c && cJSON_strcasecmp(c->string,string))
- c=c->next;
- return c;
- }
- //在鏈表中插入一個新結點
- void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem)
- {
- cJSON *c=array->child;
- // 找到which位置
- while (c && which>0)
- c=c->next,which--;
- // 添加新的節點到array中
- if (!c)
- {
- cJSON_AddItemToArray(array,newitem);
- return;
- }
- // 將鏈表節點進行掛接
- newitem->next=c;
- newitem->prev=c->prev;
- c->prev=newitem;
- // 處理arrya的孩子節點
- if (c==array->child)
- array->child=newitem;
- else
- newitem->prev->next=newitem;
- }
- // 替換節點操作,用新的節點替換原有的某一個節點
- void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem)
- {
- cJSON *c=array->child;
- // 找到which位置
- while (c && which>0)
- c=c->next,which--;
- if (!c)
- return;
- // 進行掛接
- newitem->next=c->next;
- newitem->prev=c->prev;
- // 處理NULL情況
- if (newitem->next)
- newitem->next->prev=newitem;
- // 處理孩子節點
- if (c==array->child)
- array->child=newitem;
- else
- newitem->prev->next=newitem;
- c->next=c->prev=0;
- // 刪除替換的節點
- cJSON_Delete(c);
- }
- // 替換節點操作
- // 用原有節點替換現有節點
- void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem)
- {
- int i=0;
- cJSON *c=object->child;
- while(c && cJSON_strcasecmp(c->string,string))
- i++,c=c->next;
- if(c)
- {
- newitem->string=cJSON_strdup(string);
- cJSON_ReplaceItemInArray(object,i,newitem);
- }
- }
- // 拷貝副本操作
- cJSON *cJSON_Duplicate(cJSON *item,int recurse)
- {
- cJSON *newitem,*cptr,*nptr=0,*newchild;
- /* Bail on bad ptr */
- if (!item)
- return 0;
- /* Create new item */
- newitem=cJSON_New_Item();
- if (!newitem)
- return 0;
- /* Copy over all vars */
- newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
- if (item->valuestring)
- {
- newitem->valuestring=cJSON_strdup(item->valuestring);
- if (!newitem->valuestring)
- {
- cJSON_Delete(newitem);
- return 0;
- }
- }
- if (item->string)
- {
- newitem->string=cJSON_strdup(item->string);
- if (!newitem->string)
- {
- cJSON_Delete(newitem);
- return 0;
- }
- }
- /* If non-recursive, then we're done! */
- if (!recurse)
- return newitem;
- /* Walk the ->next chain for the child. */
- cptr=item->child;
- while (cptr)
- {
- newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */
- if (!newchild)
- {
- cJSON_Delete(newitem);
- return 0;
- }
- if (nptr)
- {
- nptr->next=newchild,newchild->prev=nptr;
- nptr=newchild;
- } /* If newitem->child already set, then crosswire ->prev and ->next and move on */
- else
- {
- newitem->child=newchild;
- nptr=newchild;
- } /* Set newitem->child and move to it */
- cptr=cptr->next;
- }
- return newitem;
- }