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,文件擴(kuò)展名是 .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),紀(jì)錄(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語言項目進(jìn)行學(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)體,類型信息通過標(biāo)識符 type來進(jìn)行判斷,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ù)下標(biāo)獲取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來進(jìn)行初始化。

/* 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ù)進(jìn)行真正的解析,該函數(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) 
     * 這一步主要是為了確定字符串的長度,以便下一步進(jìn)行內(nèi)存分配
     * 問題在于字符串中可能存在一定的轉(zhuǎn)義字符,由于json字符串本身
     * 是一個字符串,碰到像雙引號必須進(jìn)行轉(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”不一定用于標(biāo)記輸入的結(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; /* 達(dá)到數(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é)束了。下篇文章我會進(jìn)行簡單的代碼示例,進(jìn)行展示cJSON的用法與封裝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容