JSON的簡單介紹&cJSON庫使用(一)

JSON WIKI解釋

JSONJavaScript Object Notation蛉鹿,JavaScript對象表示法)是一種由道格拉斯·克羅克福特構(gòu)想和設(shè)計、輕量級數(shù)據(jù)交換語言趾盐,該語言以易于讓人閱讀的文字為基礎(chǔ),用來傳輸由屬性值或者序列性的值組成的數(shù)據(jù)對象衩藤。盡管JSON是JavaScript的一個子集阐枣,但JSON是獨立于語言的文本格式甫贯,并且采用了類似于C語言家族的一些習(xí)慣吼鳞。

JSON 數(shù)據(jù)格式與語言無關(guān),脫胎自JavaScript叫搁,但當(dāng)前很多編程語言都支持 JSON 格式數(shù)據(jù)的生成和解析赔桌。JSON 的官方 MIME 類型application/json,文件擴展名是 .json渴逻。

JSON 解析器和 JSON 庫支持許多不同的編程語言疾党。 JSON 文本格式在語法上與創(chuàng)建 JavaScript 對象的代碼相同。 由于這種相似性惨奕, 無需解析器雪位, JavaScript 程序能夠使用內(nèi)建的 eval() 函數(shù), 用 JSON 數(shù)據(jù)來生成原生的 JavaScript 對象梨撞。

  • JSON 是存儲和交換文本信息的語法雹洗。 類似 XML。 JSON 比 XML 更小聋袋、 更快队伟, 更易解析穴吹。

  • JSON 具有自我描述性幽勒, 語法簡潔, 易于理解港令。

JSON建構(gòu)于兩種結(jié)構(gòu):

  • “名稱/值”對的集合(A collection of name/value pairs)啥容。不同的語言中锈颗,它被理解為對象(object),紀錄(record)咪惠,結(jié)構(gòu)(struct)击吱,字典(dictionary),哈希表(hash table)遥昧,有鍵列表(keyed list)覆醇,或者關(guān)聯(lián)數(shù)組 (associative array)。

  • 值的有序列表(An ordered list of values)炭臭。在大部分語言中永脓,它被理解為數(shù)組(array)。

JSON的三種語法:

鍵/值對 key:value鞋仍,用半角冒號分割常摧。 比如 "name":"Faye" 文檔對象 JSON對象寫在花括號中,可以包含多個鍵/值對威创。比如{ "name":"Faye" ,"address":"北京" }落午。   數(shù)組 JSON 數(shù)組在方括號中書寫: 數(shù)組成員可以是對象,值肚豺,也可以是數(shù)組(只要有意義)溃斋。{"love": ["乒乓球","高爾夫","斯諾克","羽毛球","LOL","撩妹"]}

json.jpg

cJSON是C語言中的一個JSON編解碼器,非常輕量級详炬,C文件只有不到一千行盐类,代碼的可讀性也很好,很適合作為C語言項目進行學(xué)習(xí)呛谜。
項目主頁
項目github主頁

cJSON源碼解析在跳、下載與安裝:

git clone https://github.com/DaveGamble/cJSON 
#下載完成后,切換至下載目錄
mkdir build
cd build
cmake .. -DENABLE_CJSON_UTILS=Off -DENABLE_CJSON_TEST=On -DCMAKE_INSTALL_PREFIX=/usr (生成bin+lib)
cmake .. -DENABLE_CJSON_UTILS=Off -DENABLE_CJSON_TEST=On -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_SHARED_LIBS=Off (生成bin)
make
sudo make install (安裝libcjson.so)

先來看一下cJSON的數(shù)據(jù)結(jié)構(gòu):

/* The cJSON structure: */
typedef struct cJSON {
    struct cJSON *next,*prev;   /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
    struct cJSON *child;        /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */

   int type;                   /* The type of the item, as above. */

    char *valuestring;          /* The item's string, if type==cJSON_String */
    int valueint;               /* The item's number, if type==cJSON_Number */
    double valuedouble;         /* The item's number, if type==cJSON_Number */

    char *string;               /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;

不管是數(shù)值類型隐岛、字符串類型或者對象類型等都使用該結(jié)構(gòu)體猫妙,類型信息通過標識符 type來進行判斷,cJSON總共定義了7種類型:

/* cJSON Types: */
#define cJSON_Invalid (0)
#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_Raw    (1 << 7) /* raw json */

另外聚凹,如果是對象或者數(shù)組割坠,采用的是雙向鏈表來實現(xiàn),鏈表中的每一個節(jié)點表示數(shù)組中的一個元素或者對象中的一個字段妒牙。其中child表示頭結(jié)點彼哼,next、prev分別表示下一個節(jié)點和前一個節(jié)點湘今。valuestring敢朱、valueint、valuedouble分別表示字符串、整數(shù)拴签、浮點數(shù)的字面量孝常。string表示對象中某一字段的名稱,比如有這樣的一個json字符串:

{'age': 20}//'age'則用結(jié)構(gòu)體中的string來表示蚓哩。

一构灸、cjson常用函數(shù)

用到的函數(shù),在cJSON.h中都能找到:

/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);//從 給定的json字符串中得到cjson對象
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char  *cJSON_Print(cJSON *item);//從cjson對象中獲取有格式的json對象
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char  *cJSON_PrintUnformatted(cJSON *item);//從cjson對象中獲取無格式的json對象

/* Delete a cJSON entity and all subentities. */
extern void   cJSON_Delete(cJSON *c);//刪除cjson對象岸梨,釋放鏈表占用的內(nèi)存空間

/* Returns the number of items in an array (or object). */
extern int    cJSON_GetArraySize(cJSON *array);//獲取cjson對象數(shù)組成員的個數(shù)
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);//根據(jù)下標獲取cjosn對象數(shù)組中的對象
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);//根據(jù)鍵獲取對應(yīng)的值(cjson對象)

/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);//獲取錯誤字符串

二喜颁、cjson源碼解析

簡單示例代碼如下:

char *out;cJSON *json;
    
json=cJSON_Parse(text);
if (!json) {
    printf("Error before: [%s]\n",cJSON_GetErrorPtr());
} else {
    out=cJSON_Print(json);
    cJSON_Delete(json);
    printf("%s\n",out);
    free(out);
}

下面我們先來看一下json字符串的解析,json的字符串的解析主要是通過cJSON_Parse函數(shù)來完成曹阔,打開cJSON_Parse函數(shù)后洛巢,我們發(fā)現(xiàn)該函數(shù)使用了另外一個輔助函數(shù):

/* Default options for cJSON_Parse */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
{
    return cJSON_ParseWithOpts(value, 0, 0);
}

cJSON_ParseWithOpts提供了一些額外的參數(shù)選項:

/* Parse an object - create a new root, and populate. */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
{
    parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
    cJSON *item = NULL;

    /* reset error position */
    global_error.json = NULL;
    global_error.position = 0;

    if (value == NULL)
    {
        goto fail;
    }

    buffer.content = (const unsigned char*)value;
    buffer.length = strlen((const char*)value) + sizeof("");
    buffer.offset = 0;
    buffer.hooks = global_hooks;

    item = cJSON_New_Item(&global_hooks);//創(chuàng)建一個節(jié)點,malloc分配一塊內(nèi)存次兆,再將分配的內(nèi)存初始化
    if (item == NULL) /* memory fail */
    {
        goto fail;
    }

    if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
    {
        /* parse failure. ep is set. */
        goto fail;
    }

    /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
    if (require_null_terminated)
    {
        buffer_skip_whitespace(&buffer);
        if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
        {
            goto fail;
        }
    }
    if (return_parse_end)
    {
        *return_parse_end = (const char*)buffer_at_offset(&buffer);
    }

    return item;

fail:
    if (item != NULL)
    {
        cJSON_Delete(item);
    }

    if (value != NULL)
    {
        error local_error;
        local_error.json = (const unsigned char*)value;
        local_error.position = 0;

        if (buffer.offset < buffer.length)
        {
            local_error.position = buffer.offset;
        }
        else if (buffer.length > 0)
        {
            local_error.position = buffer.length - 1;
        }

        if (return_parse_end != NULL)
        {
            *return_parse_end = (const char*)local_error.json + local_error.position;
        }

        global_error = local_error;
    }

    return NULL;
}

第一步:先調(diào)用cJSON_New_Item創(chuàng)建一個節(jié)點稿茉,該函數(shù)實現(xiàn)非常簡單,就是使用malloc分配一塊內(nèi)存芥炭,再將分配的內(nèi)存使用0來進行初始化漓库。

/* Internal constructor. */

#define internal_malloc malloc
#define internal_free free
#define internal_realloc realloc
#endif

static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
//上方可從cJSON.c中查找到引用
static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
{
    cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
    if (node)
    {
        memset(node, '\0', sizeof(cJSON));
    }

    return node;
}

第二步:調(diào)用parse_value函數(shù)進行真正的解析,該函數(shù)是json解析的核心部分园蝠,后面我們會重點分析渺蒿。而在解析前,先對json字符串調(diào)用了一次buffer_skip_whitespace彪薛,其實就是將字符串前面的的一些空字符去除茂装,代碼如下:

/* Utility to jump whitespace and cr/lf */
static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
{
    if ((buffer == NULL) || (buffer->content == NULL))
    {
        return NULL;
    }

    while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
    {
       buffer->offset++;
    }

    if (buffer->offset == buffer->length)
    {
        buffer->offset--;
    }

    return buffer;
}

最后一步:函數(shù)中參數(shù)中提供了require_null_terminated是為了確保json字符串必須以'\0'字符作為結(jié)尾。若參數(shù)提供了return_parse_end善延,將返回json字符串解析完成后剩余的部分少态。

下面來看json解析算法的核心部分:


/* Parser core - when encountering text, process appropriately. */
static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
{
    if ((input_buffer == NULL) || (input_buffer->content == NULL))
    {
        return false; /* no input */
    }

    /*分析不同類型的值 */
    /* null */
    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
    {
        item->type = cJSON_NULL;
        input_buffer->offset += 4;
        return true;
    }
    /* false */
    if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
    {
        item->type = cJSON_False;
        input_buffer->offset += 5;
        return true;
    }
    /* true */
    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
    {
        item->type = cJSON_True;
        item->valueint = 1;
        input_buffer->offset += 4;
        return true;
    }
    /* string */
    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
    {
        return parse_string(item, input_buffer);
    }
    /* number */
    if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
    {
        return parse_number(item, input_buffer);
    }
    /* array */
    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
    {
        return parse_array(item, input_buffer);
    }
    /* object */
    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
    {
        return parse_object(item, input_buffer);
    }

    return false;
}

上述 代碼看上去應(yīng)該清晰易懂。對于json為null易遣、true或者false的情況彼妻,直接將type置為對應(yīng)的類型即可。對于其他情況豆茫,需要分別再做處理侨歉,先來看json為字符串的情況:

    /* string */
    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
    {
        return parse_string(item, input_buffer);
    }
/* Parse the input text into an unescaped cinput, and populate item. */
static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
{
    const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
    const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
    unsigned char *output_pointer = NULL;
    unsigned char *output = NULL;

    /* not a string */
    if (buffer_at_offset(input_buffer)[0] != '\"')
    {
        goto fail;
    }

    {
    /* calculate approximate size of the output (overestimate) 
     * 這一步主要是為了確定字符串的長度,以便下一步進行內(nèi)存分配
     * 問題在于字符串中可能存在一定的轉(zhuǎn)義字符揩魂,由于json字符串本身
     * 是一個字符串幽邓,碰到像雙引號必須進行轉(zhuǎn)義,另外像\t這樣的轉(zhuǎn)義
     * 字符火脉,需要再次轉(zhuǎn)義牵舵,用\\t來表示茅特。而在解析的時候,我們不需要
     * 這些多余的轉(zhuǎn)義字符棋枕。(說得有點繞,不知道能不能看明白)
     */
        size_t allocation_length = 0;
        size_t skipped_bytes = 0;
        while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
        {
            /* is escape sequence */
            if (input_end[0] == '\\')
            {
                if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
                {
                    /* prevent buffer overflow when last input character is a backslash */
                    goto fail;
                }
                skipped_bytes++;
                input_end++;
            }
            input_end++;
        }
        if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
        {
            goto fail; /* string ended unexpectedly */
        }

        /* This is at most how much we need for the output */
        allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
        output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
        if (output == NULL)
        {
            goto fail; /* allocation failure */
        }
    }

    output_pointer = output;
    /* loop through the string literal */
    while (input_pointer < input_end)
    {
        if (*input_pointer != '\\')
        {
            *output_pointer++ = *input_pointer++;
        }
        /* escape sequence */
        else
        {
            unsigned char sequence_length = 2;
            if ((input_end - input_pointer) < 1)
            {
                goto fail;
            }

            switch (input_pointer[1])
            {
                case 'b':
                    *output_pointer++ = '\b';
                    break;
                case 'f':
                    *output_pointer++ = '\f';
                    break;
                case 'n':
                    *output_pointer++ = '\n';
                    break;
                case 'r':
                    *output_pointer++ = '\r';
                    break;
                case 't':
                    *output_pointer++ = '\t';
                    break;
                case '\"':
                case '\\':
                case '/':
                    *output_pointer++ = input_pointer[1];
                    break;

                /* UTF-16 literal */
                case 'u':
                    sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
                    if (sequence_length == 0)
                    {
                        /* failed to convert UTF16-literal to UTF-8 */
                        goto fail;
                    }
                    break;

                default:
                    goto fail;
            }
            input_pointer += sequence_length;
        }
    }

    /* zero terminate the output */
    *output_pointer = '\0';

    item->type = cJSON_String;
    item->valuestring = (char*)output;

    input_buffer->offset = (size_t) (input_end - input_buffer->content);
    input_buffer->offset++;

    return true;

fail:
    if (output != NULL)
    {
        input_buffer->hooks.deallocate(output);
    }

    if (input_pointer != NULL)
    {
        input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
    }

    return false;
}

下面是解析數(shù)值類型的代碼:

解析字符串的困難之處在于可能會碰到轉(zhuǎn)義字符妒峦,需要將類似于\t這樣的再轉(zhuǎn)義字符轉(zhuǎn)化為\t.

下面是解析數(shù)值類型的代碼:


/* Parse the input text to generate a number, and populate the result into item. */
static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
{
    double number = 0;
    unsigned char *after_end = NULL;
    unsigned char number_c_string[64];
    unsigned char decimal_point = get_decimal_point();
    size_t i = 0;

    if ((input_buffer == NULL) || (input_buffer->content == NULL))
    {
        return false;
    }

    /* 將數(shù)字復(fù)制到臨時緩沖區(qū)中重斑,并將“.”替換為當(dāng)前區(qū)域設(shè)置的小數(shù)點(對于strtod)
     * 這還需要注意“0”不一定用于標記輸入的結(jié)尾。  
     */
    for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
    {
        switch (buffer_at_offset(input_buffer)[i])
        {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '+':
            case '-':
            case 'e':
            case 'E':
                number_c_string[i] = buffer_at_offset(input_buffer)[i];
                break;

            case '.':
                number_c_string[i] = decimal_point;
                break;

            default:
                goto loop_end;
        }
    }
loop_end:
    number_c_string[i] = '\0';

    number = strtod((const char*)number_c_string, (char**)&after_end);
    if (number_c_string == after_end)
    {
        return false; /* parse_error */
    }

    item->valuedouble = number;

    /* use saturation in case of overflow */
    if (number >= INT_MAX)
    {
        item->valueint = INT_MAX;
    }
    else if (number <= (double)INT_MIN)
    {
        item->valueint = INT_MIN;
    }
    else
    {
        item->valueint = (int)number;
    }

    item->type = cJSON_Number;

    input_buffer->offset += (size_t)(after_end - number_c_string);
    return true;
}

考慮了小數(shù)和指數(shù)的情況肯骇,會帶來一定的復(fù)雜性窥浪。

接下來是如何解析數(shù)組:

/* Build an array from input text. */
static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
{
    cJSON *head = NULL; /* head of the linked list */
    cJSON *current_item = NULL;

    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
    {
        return false; /* to deeply nested */
    }
    input_buffer->depth++;

    if (buffer_at_offset(input_buffer)[0] != '[')
    {
        /* 不是一個數(shù)組,直接返回 */
        goto fail;
    }

    input_buffer->offset++;
    buffer_skip_whitespace(input_buffer);//跳過前面的空白字符
    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
    {
       /* 空數(shù)組 */
        goto success;
    }

    /* 檢查是否跳過緩沖區(qū)結(jié)尾 */
    if (cannot_access_at_index(input_buffer, 0))
    {
        input_buffer->offset--;
        goto fail;
    }

    /* 返回到第一個元素前面的字符 */
    input_buffer->offset--;
    /* 循環(huán)遍歷逗號分隔的數(shù)組元素  */
    do
    {
        /* allocate next item */
        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
        if (new_item == NULL)
        {
            goto fail; /* allocation failure */
        }

        /* attach next item to list */
        if (head == NULL)
        {
            /* start the linked list */
            current_item = head = new_item;
        }
        else
        {
            /*放在child節(jié)點的尾部笛丙,形成一個鏈表 */
            current_item->next = new_item;
            new_item->prev = current_item;
            current_item = new_item;
        }

        /* parse next value */
        input_buffer->offset++;
        buffer_skip_whitespace(input_buffer);
        if (!parse_value(current_item, input_buffer))
        {
            goto fail; /* failed to parse value */
        }
        buffer_skip_whitespace(input_buffer);
    }
    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));

    if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
    {
        goto fail; /* 達到數(shù)組的尾部  */
    }

success:
    input_buffer->depth--;

    item->type = cJSON_Array;
    item->child = head;

    input_buffer->offset++;

    return true;

fail:
    if (head != NULL)
    {
        cJSON_Delete(head);
    }

    return false;
}

解析數(shù)組時漾脂,其基本思想是對數(shù)組中每一個元素遞歸調(diào)用parse_value,再將這些元素連接形成一個鏈表胚鸯。

如果能看懂?dāng)?shù)組的解析過程骨稿,對象的解析直接來看代碼:

/* Build an object from the text. */
static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
{
    cJSON *head = NULL; /* linked list head */
    cJSON *current_item = NULL;

    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
    {
        return false; /* to deeply nested */
    }
    input_buffer->depth++;

    if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
    {
        goto fail; /*  不是一個對象,直接返回 */
    }

    input_buffer->offset++;
    buffer_skip_whitespace(input_buffer);
    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
    {
        goto success; /* empty object */
    }

    /* 檢查是否跳過緩沖區(qū)結(jié)尾 */
    if (cannot_access_at_index(input_buffer, 0))
    {
        input_buffer->offset--;
        goto fail;
    }

    /* 返回到第一個元素前面的字符 */
    input_buffer->offset--;
    /* 循環(huán)遍歷逗號分隔的數(shù)組元素  */
    do
    {
        /* allocate next item */
        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
        if (new_item == NULL)
        {
            goto fail; /* allocation failure */
        }

        /* attach next item to list */
        if (head == NULL)
        {
            /* start the linked list */
            current_item = head = new_item;
        }
        else
        {
            /* add to the end and advance */
            current_item->next = new_item;
            new_item->prev = current_item;
            current_item = new_item;
        }

        /* parse the name of the child */
        input_buffer->offset++;
        buffer_skip_whitespace(input_buffer);
        if (!parse_string(current_item, input_buffer))
        {
            goto fail; /* faile to parse name */
        }
        buffer_skip_whitespace(input_buffer);

        /* 交換valuestring和string姜钳,因為我們分析了名稱 */
        current_item->string = current_item->valuestring;
        current_item->valuestring = NULL;

        if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
        {
            goto fail; /* invalid object */
        }

        /* parse the value */
        input_buffer->offset++;
        buffer_skip_whitespace(input_buffer);
        if (!parse_value(current_item, input_buffer))
        {
            goto fail; /* failed to parse value */
        }
        buffer_skip_whitespace(input_buffer);
    }
    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));

    if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
    {
        goto fail; /* expected end of object */
    }

success:
    input_buffer->depth--;

    item->type = cJSON_Object;
    item->child = head;

    input_buffer->offset++;
    return true;

fail:
    if (head != NULL)
    {
        cJSON_Delete(head);
    }

    return false;
}

客戶端代碼在使用完成后坦冠,需要將分配的內(nèi)存釋放掉:

void cJSON_Delete(cJSON *c) {
    cJSON *next;
    while (c) { //循環(huán)刪除鏈表中所有節(jié)點
        next = c->next;
        if (c->child) cJSON_Delete(c->child); //存在子節(jié)點,遞歸刪除
        if (c->valuestring) cJSON_free(c->valuestring); 
        if (c->string) cJSON_free(c->string);
        cJSON_free(c); //刪除結(jié)構(gòu)體本身
        c = next;
    }
}

cJSON對于json字符串的解析基本就結(jié)束了哥桥。下篇文章我會進行簡單的代碼示例辙浑,進行展示cJSON的用法與封裝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拟糕,一起剝皮案震驚了整個濱河市判呕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌送滞,老刑警劉巖侠草,帶你破解...
    沈念sama閱讀 221,406評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異犁嗅,居然都是意外死亡梦抢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評論 3 398
  • 文/潘曉璐 我一進店門愧哟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奥吩,“玉大人,你說我怎么就攤上這事蕊梧∠己眨” “怎么了?”我有些...
    開封第一講書人閱讀 167,815評論 0 360
  • 文/不壞的土叔 我叫張陵肥矢,是天一觀的道長端衰。 經(jīng)常有香客問我叠洗,道長,這世上最難降的妖魔是什么旅东? 我笑而不...
    開封第一講書人閱讀 59,537評論 1 296
  • 正文 為了忘掉前任灭抑,我火速辦了婚禮,結(jié)果婚禮上抵代,老公的妹妹穿的比我還像新娘腾节。我一直安慰自己,他們只是感情好荤牍,可當(dāng)我...
    茶點故事閱讀 68,536評論 6 397
  • 文/花漫 我一把揭開白布案腺。 她就那樣靜靜地躺著,像睡著了一般康吵。 火紅的嫁衣襯著肌膚如雪劈榨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,184評論 1 308
  • 那天晦嵌,我揣著相機與錄音同辣,去河邊找鬼。 笑死惭载,一個胖子當(dāng)著我的面吹牛邑闺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棕兼,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼陡舅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了伴挚?” 一聲冷哼從身側(cè)響起靶衍,我...
    開封第一講書人閱讀 39,668評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茎芋,沒想到半個月后颅眶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,212評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡田弥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,299評論 3 340
  • 正文 我和宋清朗相戀三年涛酗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偷厦。...
    茶點故事閱讀 40,438評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡商叹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出只泼,到底是詐尸還是另有隱情剖笙,我是刑警寧澤,帶...
    沈念sama閱讀 36,128評論 5 349
  • 正文 年R本政府宣布请唱,位于F島的核電站弥咪,受9級特大地震影響过蹂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜聚至,卻給世界環(huán)境...
    茶點故事閱讀 41,807評論 3 333
  • 文/蒙蒙 一酷勺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扳躬,春花似錦脆诉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狂鞋。三九已至片择,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間骚揍,已是汗流浹背字管。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留滤否,地道東北人块请。 一個月前我還...
    沈念sama閱讀 48,827評論 3 376
  • 正文 我出身青樓姐扮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親硫戈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,446評論 2 359