cJSON的使用

1 安裝cJSON

github地址://github.com/DaveGamble/cJSON.git

  1. 下載完成後進入cJSON目錄,執行下面命令生成Makefile文件

    mkdir build
    cd build
    cmake ..
    
  2. 執行下面命令安裝cJSON庫

    make install
    
    • 默認將頭文件安裝到/usr/local/include/cjson路徑下
    • 默認將鏈接庫安裝到/usr/local/lib路徑下
  3. 一些CMake選項介紹

    選項 功能 默認值
    -DENABLE_CJSON_TEST 開啟編譯tests On
    -DENABLE_CJSON_UTILS 開啟編譯cJSON_Utils Off
    -DENABLE_TARGET_EXPORT 開啟CMake目標導出 On
    -DENABLE_CUSTOM_COMPILER_FLAGS 開啟自定義編譯選項 On
    -DENABLE_VALGRIND 使用valgrind運行tests Off
    -DENABLE_SANITIZERS 使用AddressSanitizer和UndefinedBehaviorSanitizer編譯cJSON Off
    -DENABLE_SAFE_STACK 開啟SafeStack工具檢測(目前只能在Clang編譯器上工作) Off
    -DBUILD_SHARED_LIBS 編譯動態庫 On
    -DBUILD_SHARED_AND_STATIC_LIBS 編譯動態庫和靜態庫 Off
    -DCMAKE_INSTALL_PREFIX 設置安裝路徑前綴
    -DENABLE_LOCALES 開啟使用localeconv方法 On
    -DCJSON_OVERRIDE_BUILD_SHARED_LIBS 使用-DCJSON_BUILD_SHARED_LIBS覆蓋BUILD_SHARED_LIBS的值
  4. 在程式碼中引入頭文件

    #include <cjson/cJSON.h>
    

2 cJSON數據結構

  • cJSON使用cJSON結構體表示JSON數據

    typedef struct cJSON
    {
        /* next和prev允許你遍歷array/object鏈,或者使用GetArraySize/GetArrayItem/GetObjectItem方法 */
        struct cJSON *next;
        struct cJSON *prev;
        /* array/object會擁有child指針,指向它包含的一連串元素 */
        struct cJSON *child;
    
        /* 類型 */
        int type;
    
        /* 如果type為cJSON_String或cJSON_Raw,使用它保存string值 */
        char *valuestring;
        /* 將值寫入valueint已過時,使用cJSON_SetNumberValue代替 */
        int valueint;
        /* 如果type為cJSON_Number,使用它保存number值 */
        double valuedouble;
        /* 如果該節點是array或者object的子元素,則使用它保存名稱字元串 */
        char *string;
    } cJSON;
    
  • type取值

    取值 含義 備註 檢查
    cJSON_Invalid 非法 不包含任何值 cJSON_IsInvalid
    cJSON_False false cJSON_IsFalse/cJSON_IsBool
    cJSON_True true cJSON_IsTrue/cJSON_IsBool
    cJSON_NULL null cJSON_IsNull
    cJSON_Number 數字 值將以double類型保存在valuedouble中,同時也保存在valueint中,如果超出整形範圍,則valueint值為INT_MAX或者INT_MIN cJSON_IsNumber
    cJSON_String 字元串 '\0'結尾的形式保存在valuestring cJSON_IsString
    cJSON_Array 數組 child指針指向數組中各個元素,各元素通過next/prev指針串在一起形成鏈表 cJSON_IsArray
    cJSON_Object 對象 同數組保存形式相同,只不過對象中的各個元素會將它們的key值存儲在string cJSON_IsObject
    cJSON_Raw 任意JSON類型 '\0'結尾的形式保存在valuestring中,使用cJSON解析時不會生成這個類型,並且cJSON不會校驗JSON的合法性 cJSON_IsRaw
    cJSON_IsReference 引用 child指向的元素或者valuestring不被當前節點擁有,當使用cJSON_Delete時,只會釋放當前節點,而不會釋放它的childvaluestring
    cJSON_StringIsConst 常量字元串 string指向一個常量字元串,當使用cJSON_Delete時,將不會釋放string

3 創建cJSON數據

對於每種type,都有一個與之對應的cJSON_Create...方法用於創建該類型的節點,所有方法都會創建一個cJSON結構體,可以使用cJSON_Delete釋放它

注意:

  • 不能使用cJSON_Delete釋放array或object當中的元素,當array或者object被釋放時,其中的元素也會自動釋放
  • 可以使用cJSON_SetValueString改變cJSON_String節點的valuestring值,而不需要手動釋放之前的valuestring

3.1 基本類型

類型 創建方法 說明
null cJSON_CreateNull
boolean cJSON_CreateTrue/cJSON_CreateFalse/cJSON_CreateBool
number cJSON_CreateNumber 將同時設置valuedoublevalueint,如果數字的值超過整形範圍,則valueint將被設為INT_MAXINT_MIN
string cJSON_CreateString/cJSON_CreateStringReference cJSON_CreateString會拷貝字元串,而cJSON_CreateStringReference直接指向設置的字元串

3.2 數組

3.2.1 創建數組

  • cJSON_CreateArray:創建一個空數組
  • cJSON_CreateArrayReference:創建一個數組,但是它裡面的所有元素都不屬於它自己,所以不能使用cJSON_Delete刪除它裡面的內容

3.2.2 向數組中添加元素

  • cJSON_AddItemToArray:在數組尾端添加元素
  • cJSON_AddItemReferenceToArray:將另一個元素的引用添加進數組
  • cJSON_InsertItemInArray:在指定索引處插入元素

3.2.3 獲取數組中的元素

  • cJSON_GetArrayItem:根據索引獲取元素

  • cJSON_DetachItemFromArray:根據索引獲取數組中的元素,並將其從數組中分離,以便後續能夠繼續使用

3.2.4 刪除數組中的元素

  • cJSON_DeleteItemFromArray:根據索引刪除數組中的元素

3.2.5 替換數組中的元素

  • cJSON_ReplaceItemInArray:根據索引替換元素
  • cJSON_ReplaceItemViaPointer:根據指針替換元素,失敗返回0

3.2.6 獲取數組的大小

  • cJSON_GetArraySize:獲取數組的大小

3.2.7 迭代數組中的元素

  • cJSON_ArrayForEach:在\(O(n)\)的時間內迭代數組中的元素

3.3 對象

3.3.1 創建對象

  • cJSON_CreateObject:創建一個空對象
  • cJSON_CreateObjectReference:創建一個對象,但是它裡面的所有元素都不屬於它自己,所以不能使用cJSON_Delete刪除它裡面的內容

3.3.2 向對象中添加元素

  • cJSON_AddItemToObject:向對象中添加一個元素
  • cJSON_AddItemToObjectCS:向對象中添加一個名稱(對象的key,即cJSON結構體中的string)為常量或者引用的元素
  • cJSON_AddItemReferenceToObject:將另一個元素的引用添加進對象

3.3.3 獲取對象中的元素

  • cJSON_GetObjectItemCaseSensitive:獲取對象中的元素

  • cJSON_DetachItemFromObjectCaseSensitive:分離並獲取對象中的元素

3.3.4 刪除對象中的元素

  • cJSON_DeleteItemFromObjectCaseSensitive:刪除對象中的元素

3.3.5 替換對象中的元素

  • cJSON_ReplaceItemInObjectCaseSensitive:根據key替換對象中的元素
  • cJSON_ReplaceItemViaPointer:根據指針替換對象中的元素,失敗返回0

3.3.6 獲取對象的大小

  • cJSON_GetArraySize:獲取對象的大小

3.3.7 迭代對象中的元素

  • cJSON_ArrayForEach:迭代對象中的元素

3.3.8 快速創建並添加元素到對象

cJSON提供了快速創建並添加到對象的方法,這些方法返回指向新創建元素的指針,如果失敗,則返回NULL

  • cJSON_AddNullToObject
  • cJSON_AddTrueToObject
  • cJSON_AddFalseToObject
  • cJSON_AddBoolToObject
  • cJSON_AddNumberToObject
  • cJSON_AddStringToObject
  • cJSON_AddRawToObject
  • cJSON_AddObjectToObject
  • cJSON_AddArrayToObject

4 cJSON解析與輸出JSON

4.1 解析JSON

cJSON提供的解析函數的返回值需要手動調用cJSON_Delete釋放

  • cJSON_Parse:解析以'\0'結尾的JSON字元串

    cJSON * cJSON_Parse(const char *value);
    
  • cJSON_ParseWithLength:解析指定長度的JSON字元串(可以不以'\0'結尾)

    cJSON * cJSON_ParseWithLength(const char *value, size_t buffer_length);
    
  • cJSON_ParseWithOpts

    cJSON * cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
    
    • return_parse_end:返回指向輸入字元串中JSON的結束位置,或解析出錯的位置
    • require_null_terminated:是否禁止輸入字元串中的JSON後面還包含有數據
  • cJSON_ParseWithLengthOpts

    cJSON * cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
    

4.2 輸出JSON

cJSON提供的輸出函數的返回值需要手動調用free釋放

  • cJSON_Print:輸出經過空白字元格式化後的JSON

    char * cJSON_Print(const cJSON *item);
    
  • cJSON_PrintUnformatted:輸出沒有經過空白字元格式化後的JSON

    char * cJSON_PrintUnformatted(const cJSON *item);
    
  • cJSON_PrintBuffered:輸出JSON到指定大小的buffer

    char * cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
    
    • prebuffer:指定輸出buffer的初始大小
    • fmt:是否使用空白字元進行格式化
  • cJSON_PrintPreallocated:輸出JSON到靜態buffer,從而避免動態分配記憶體,當buffer大小不夠時,調用失敗,返回0,成功時返回1

    cJSON_bool cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
    

    注意:至少提供5位元組大小的buffer

5 cJSON使用示例

{
    "name": "Awesome 4K",
    "resolutions": [
        {
            "width": 1280,
            "height": 720
        },
        {
            "width": 1920,
            "height": 1080
        },
        {
            "width": 3840,
            "height": 2160
        }
    ]
}

5.1 列印輸出JSON

5.1.1 方法1

//create a monitor with a list of supported resolutions
//NOTE: Returns a heap allocated string, you are required to free it after use.
char *create_monitor(void)
{
    const unsigned int resolution_numbers[3][2] = {
        {1280, 720},
        {1920, 1080},
        {3840, 2160}
    };
    char *string = NULL;
    cJSON *name = NULL;
    cJSON *resolutions = NULL;
    cJSON *resolution = NULL;
    cJSON *width = NULL;
    cJSON *height = NULL;
    size_t index = 0;

    cJSON *monitor = cJSON_CreateObject();
    if (monitor == NULL)
    {
        goto end;
    }

    name = cJSON_CreateString("Awesome 4K");
    if (name == NULL)
    {
        goto end;
    }
    /* after creation was successful, immediately add it to the monitor,
     * thereby transferring ownership of the pointer to it */
    cJSON_AddItemToObject(monitor, "name", name);

    resolutions = cJSON_CreateArray();
    if (resolutions == NULL)
    {
        goto end;
    }
    cJSON_AddItemToObject(monitor, "resolutions", resolutions);

    for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index)
    {
        resolution = cJSON_CreateObject();
        if (resolution == NULL)
        {
            goto end;
        }
        cJSON_AddItemToArray(resolutions, resolution);

        width = cJSON_CreateNumber(resolution_numbers[index][0]);
        if (width == NULL)
        {
            goto end;
        }
        cJSON_AddItemToObject(resolution, "width", width);

        height = cJSON_CreateNumber(resolution_numbers[index][1]);
        if (height == NULL)
        {
            goto end;
        }
        cJSON_AddItemToObject(resolution, "height", height);
    }

    string = cJSON_Print(monitor);
    if (string == NULL)
    {
        fprintf(stderr, "Failed to print monitor.\n");
    }

end:
    cJSON_Delete(monitor);
    return string;

5.1.2 方法2

使用cJSON_Add...ToObject快捷創建和添加元素

//NOTE: Returns a heap allocated string, you are required to free it after use.
char *create_monitor_with_helpers(void)
{
    const unsigned int resolution_numbers[3][2] = {
        {1280, 720},
        {1920, 1080},
        {3840, 2160}
    };
    char *string = NULL;
    cJSON *resolutions = NULL;
    size_t index = 0;

    cJSON *monitor = cJSON_CreateObject();

    if (cJSON_AddStringToObject(monitor, "name", "Awesome 4K") == NULL)
    {
        goto end;
    }

    resolutions = cJSON_AddArrayToObject(monitor, "resolutions");
    if (resolutions == NULL)
    {
        goto end;
    }

    for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index)
    {
        cJSON *resolution = cJSON_CreateObject();

        if (cJSON_AddNumberToObject(resolution, "width", resolution_numbers[index][0]) == NULL)
        {
            goto end;
        }

        if (cJSON_AddNumberToObject(resolution, "height", resolution_numbers[index][1]) == NULL)
        {
            goto end;
        }

        cJSON_AddItemToArray(resolutions, resolution);
    }

    string = cJSON_Print(monitor);
    if (string == NULL)
    {
        fprintf(stderr, "Failed to print monitor.\n");
    }

end:
    cJSON_Delete(monitor);
    return string;
}

5.2 解析JSON

/* return 1 if the monitor supports full hd, 0 otherwise */
int supports_full_hd(const char * const monitor)
{
    const cJSON *resolution = NULL;
    const cJSON *resolutions = NULL;
    const cJSON *name = NULL;
    int status = 0;
    cJSON *monitor_json = cJSON_Parse(monitor);
    if (monitor_json == NULL)
    {
        const char *error_ptr = cJSON_GetErrorPtr();
        if (error_ptr != NULL)
        {
            fprintf(stderr, "Error before: %s\n", error_ptr);
        }
        status = 0;
        goto end;
    }

    name = cJSON_GetObjectItemCaseSensitive(monitor_json, "name");
    if (cJSON_IsString(name) && (name->valuestring != NULL))
    {
        printf("Checking monitor \"%s\"\n", name->valuestring);
    }

    resolutions = cJSON_GetObjectItemCaseSensitive(monitor_json, "resolutions");
    cJSON_ArrayForEach(resolution, resolutions)
    {
        cJSON *width = cJSON_GetObjectItemCaseSensitive(resolution, "width");
        cJSON *height = cJSON_GetObjectItemCaseSensitive(resolution, "height");

        if (!cJSON_IsNumber(width) || !cJSON_IsNumber(height))
        {
            status = 0;
            goto end;
        }

        if ((width->valuedouble == 1920) && (height->valuedouble == 1080))
        {
            status = 1;
            goto end;
        }
    }

end:
    cJSON_Delete(monitor_json);
    return status;
}