cJSON,c語言的JSON庫!

Welcome to cJSON.

 

    cJSON的目標是成爲您能夠完成工作的“最愚蠢(最便捷)”的解析器。它是一個C文件和一個頭文件。

 

    JSON它類似於XML,但不含冗餘。您可以使用它來移動數據、存儲數據,或者只是表示程序的狀態。

 

    作爲一個庫,cJSON的存在可以帶走儘可能多的跑腿工作(重複造輪子),但不會妨礙您的工作。作爲實用主義的觀點(即忽略事實),我想說你可以在兩種模式中使用它:自動模式手動模式。讓我們快速瀏覽一下。

 

Building

    有幾種方法可以將cJSON合併到您的項目中。

複製源文件

    因爲整個庫只有一個C文件和一個頭文件,所以您可以將cJSON.h和cJSON.c複製到您的項目源代碼並開始使用它。
    cJSON是用ANSI C (C89)編寫的,以支持儘可能多的平臺和編譯器。

CMake

    使用CMake, cJSON支持完整的構建系統。通過這種方式,您可以獲得最多的功能。支持與2.8.5相同或更高版本的CMake。使用CMake時,建議執行out of tree構建,即將編譯後的文件放在與源文件分開的目錄中。因此,爲了在Unix平臺上使用CMake構建cJSON,需要創建一個構建目錄並在其中運行CMake。

mkdir buildcd buildcmake ..

    這將創建一個Makefile和一堆其他文件。然後你可以編譯它:

make

    如果你想安裝的話,可以使用make install。默認情況下,它將標頭/usr/local/include/cjson和庫安裝到/usr/local/lib。它還爲pkg-config安裝文件,以便更容易地檢測和使用CMake的現有安裝。它安裝CMake配置文件,其他基於CMake的項目可以使用這些配置文件來發現庫。

 

    您可以使用可以傳遞給CMake的不同選項列表來更改構建過程,打開和關閉:

  • -DENABLE_CJSON_TEST=On:啓用構建測試。(默認情況下)

  • -DENABLE_CJSON_UTILS=On:啓用構建cJSON_Utils。(默認情況下)

  • -DENABLE_TARGET_EXPORT=On:啓用CMake目標的導出。如果有問題就關掉。(默認情況下)

  • -DENABLE_CUSTOM_COMPILER_FLAGS=On:啓用自定義編譯器標記(目前適用於Clang、GCC和MSVC)。如果有問題就關掉。(默認情況下)

  • -DENABLE_VALGRIND=On:使用valgrind運行測試。(默認情況下)

  • -DENABLE_SANITIZERS=On:在啓用了AddressSanitizer和UndefinedBehaviorSanitizer功能(如果可能的話)的情況下編譯cJSON。(默認情況下)

  • -DENABLE_SAFE_STACK:啓用SafeStack檢測傳遞。目前只適用於Clang編譯器。(默認情況下)

  • -DBUILD_SHARED_LIBS=On:構建共享庫。(默認情況下)

  • -DBUILD_SHARED_AND_STATIC_LIBS=On:構建共享庫和靜態庫。(默認情況下)

  • -DCMAKE_INSTALL_PREFIX=/usr:設置安裝的前綴。

  • -DENABLE_LOCALES=On:啓用localeconv方法。(默認開啓)

  • -DCJSON_OVERRIDE_BUILD_SHARED_LIBS=On:啓用- dcjson_build_build_shared_libs覆蓋BUILD_SHARED_LIBS的值。

 

    如果您正在爲一個Linux發行版打包cJSON,您可能會採取以下步驟:

mkdir buildcd buildcmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off -DCMAKE_INSTALL_PREFIX=/usrmakemake DESTDIR=$pkgdir install

 

    在Windows上,CMake通常用於創建Visual Studio解決方案文件,方法是在Visual Studio的開發人員命令提示符中運行它,具體步驟遵循CMake和Microsoft的官方文檔,並使用您選擇的在線搜索引擎。上述選項的描述仍然普遍適用,儘管並非所有選項都適用於Windows。

Makefile

注意:不推薦使用此方法。儘可能使用CMake。Makefile支持僅限於修復bug。

    如果你沒有可用的CMake,但仍然有GNU make。您可以使用makefile來構建cJSON:
    在帶有源代碼的目錄中運行這個命令,它將自動編譯靜態和共享庫以及一個小測試程序(不是完整的測試套件)。

make all

 

    如果需要,可以使用make install將編譯後的庫安裝到系統中。默認情況下,它將在/usr/local/include/cjson中安裝標頭,在/usr/local/lib中安裝庫。但是您可以通過設置PREFIXDESTDIR變量來更改此行爲:make PREFIX=/usr DESTDIR=temp install。然後使用:make PREFIX=/usr DESTDIR=temp uninstall來卸載它們。

Vcpkg

    你可以使用vcpkg依賴管理器下載和安裝cJSON:

git clone https://github.com/Microsoft/vcpkg.gitcd vcpkg./bootstrap-vcpkg.sh./vcpkg integrate installvcpkg install cjson

    vcpkg中的cJSON端口由Microsoft團隊成員和社區貢獻者保持最新。如果版本過期,請在vcpkg存儲庫中創建問題或拉出請求。

Including cJSON

如果你通過CMake或Makefile安裝它,你可以像這樣包含cJSON:

#include <cjson/cJSON.h>

Data Structure

    cJSON表示使用cJSON結構數據類型的JSON數據:

/* cJSON結構: */typedef struct cJSON{    struct cJSON *next;    struct cJSON *prev;    struct cJSON *child;    int type;    char *valuestring;    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */    int valueint;    double valuedouble;    char *string;} cJSON;

    這種類型的項表示JSON值。類型以位標誌的形式存儲在type中(這意味着僅通過比較type的值無法找到類型)。

    要檢查項的類型,請使用相應的cJSON_Is…函數。它執行一個NULL檢查,然後執行一個類型檢查,如果項目是這種類型,則返回一個布爾值。

    可以是以下類型之一:

  • cJSON_Invalid(使用cJSON_IsInvalid進行檢查):表示不包含任何值的無效項。如果將項設置爲所有零字節,則會自動擁有此類型。

  • cJSON_False(用cJSON_IsFalse檢查):表示一個假布爾值。您還可以使用cJSON_IsBool檢查布爾值。

  • cJSON_True(用cJSON_IsTrue檢查):表示一個真正的布爾值。您還可以使用cJSON_IsBool檢查布爾值。

  • cJSON_NULL(使用cJSON_IsNull檢查):表示一個空值。

  • cJSON_Number(用cJSON_IsNumber檢查):表示一個數值。該值在valuedouble和valueint中存儲爲double。如果該數字超出了整數的範圍,則INT_MAX或INT_MIN用於valueint。

  • cJSON_String(用cJSON_IsString檢查):表示一個字符串值。它以零終止字符串的形式存儲在valuestring中。

  • cJSON_Array(使用cJSON_IsArray檢查):表示一個數組值。這是通過將child指向一個表示數組中值的cJSON項的鏈表來實現的。這些元素使用next和prev鏈接在一起,其中第一個元素有prev。next == NULL,最後一個元素next == NULL。

  • cJSON_Object(用cJSON_IsObject檢查):表示一個對象值。對象的存儲方式與數組相同,唯一的區別是對象中的項將鍵存儲爲字符串。

  • cJSON_Raw(使用cjson_w進行檢查):表示以零結尾的字符數組形式存儲在valuestring中的任何JSON類型。例如,這可以用來避免反覆打印相同的靜態JSON以節省性能。cJSON在解析時永遠不會創建這種類型。還要注意,cJSON不會檢查它是否是有效的JSON。

    此外,還有以下兩個標誌:

  • cJSON_IsReference:指定子元素指向的項和/或valuestring不屬於這個元素,它只是一個引用。所以cJSON_Delete和其他函數將只釋放這個項目,而不是它的子/valuestring。

  • cJSON_StringIsConst:這意味着字符串指向一個常量字符串。這意味着cJSON_Delete和其他函數不會嘗試釋放字符串。

 

Working with the data structure

    對於每個值類型都有一個cJSON_Create…函數,可用於創建該類型的項。所有這些都將分配一個cJSON結構,稍後可以使用cJSON_Delete刪除它。請注意,您必須在某個時候刪除它們,否則將導致內存泄漏。

    重要提示:如果您已經向數組或對象添加了項,則不能使用cJSON_Delete刪除它。將其添加到數組或對象中會轉移其所有權,以便在刪除該數組或對象時也將其刪除。您也可以使用cJSON_SetValuestring來更改cJSON_String的valuestring,而不必手動釋放先前的valuestring。

基本類型

  • null 是用cJSON_CreateNull創建的

  • booleans 是用cJSON_CreateTrue創建的,cJSON_CreateFalse或cJSON_CreateBool

  • numbers 是用cJSON_CreateNumber創建的。這將設置valuedouble和valueint。如果數字超出了整數的範圍,則使用INT_MAX或INT_MIN來創建valueint

  • strings ,使用cJSON_CreateString(複製該字符串)或cJSON_CreateStringReference(直接指向該字符串)創建該字符串。這意味着valuestring不會被cJSON_Delete刪除,您要對它的生存期負責,這對常量很有用)

數組

    您可以使用cJSON_CreateArray創建一個空數組。cJSON_CreateArrayReference可以用來創建一個不“擁有”其內容的數組,所以它的內容不會被cJSON_Delete刪除。

 

    若要將項添加到數組中,請使用cJSON_AddItemToArray將項追加到末尾。使用cJSON_AddItemReferenceToArray可以將一個元素添加爲另一個項、數組或字符串的引用。這意味着cJSON_Delete將不會刪除那些項的子屬性或valuestring屬性,因此,如果它們已經在其他地方使用了,就不會發生重複釋放。要在中間插入項,可以使用cJSON_InsertItemInArray。它將在給定的基於0的索引處插入一個項,並將所有現有項向右移動。

 

    如果您想從一個給定索引的數組中取出一個項目並繼續使用它,那麼使用cJSON_DetachItemFromArray,它將返回分離的項目,所以一定要將它分配給一個指針,否則您將有內存泄漏。


    刪除項目是使用cJSON_DeleteItemFromArray完成的。它的工作原理類似於cJSON_DetachItemFromArray,但是通過cJSON_Delete刪除分離的項目。

 

    您還可以在適當的位置替換數組中的項。使用索引的cJSON_ReplaceItemInArray或使用給定元素指針的cJSON_ReplaceItemViaPointer。如果cJSON_ReplaceItemViaPointer失敗,它將返回0。這在內部做的是分離舊項、刪除它並在其位置插入新項。

 

    要獲得數組的大小,請使用cJSON_GetArraySize。使用cJSON_GetArrayItem獲取給定索引處的元素。


    因爲數組存儲爲一個鏈表,通過迭代索引效率低下(O (n²)),所以你可以使用cJSON_ArrayForEach宏遍歷一個數組在O (n)時間複雜度。

 

對象

    您可以使用cJSON_CreateObject創建一個空對象。cJSON_CreateObjectReference可以用來創建一個不“擁有”其內容的對象,因此它的內容不會被cJSON_Delete刪除。

 

    要向對象添加項,請使用cJSON_AddItemToObject。使用cJSON_AddItemToObjectCS向名稱爲常量或引用(該項的鍵,cJSON結構中的字符串)的對象添加項,這樣cJSON_Delete就不會釋放它。使用cJSON_AddItemReferenceToArray可以將一個元素添加爲另一個對象、數組或字符串的引用。這意味着cJSON_Delete將不會刪除那些項的子屬性或valuestring屬性,因此,如果它們已經在其他地方使用了,就不會發生重複釋放。

 

    如果你想從一個對象中取出一個項目,使用cJSON_DetachItemFromObjectCaseSensitive,它將返回分離的項目,所以一定要把它分配到一個指針,否則你會有一個內存泄漏。

 

    刪除項目是用cJSON_DeleteItemFromObjectCaseSensitive完成的。它的工作原理類似於cJSON_DetachItemFromObjectCaseSensitive,後面跟着cJSON_Delete。

 

    您還可以在適當的位置替換對象中的項。或者使用鍵使用cJSON_ReplaceItemInObjectCaseSensitive,或者使用cJSON_ReplaceItemViaPointer給出一個指向元素的指針。如果cJSON_ReplaceItemViaPointer失敗,它將返回0。這在內部做的是分離舊項、刪除它並在其位置插入新項。

 

    要獲得對象的大小,可以使用cJSON_GetArraySize,這是因爲在內部對象是作爲數組存儲的。


    如果你想訪問對象中的一個項目,使用cJSON_GetObjectItemCaseSensitive。


    要在對象上進行迭代,可以使用cJSON_ArrayForEach宏,方法與數組相同。


    cJSON還提供了方便的幫助函數,用於快速創建新項並將其添加到對象中,如cJSON_AddNullToObject。它們返回指向新項的指針,如果失敗則返回NULL。

 

解析JSON

    給定以零結尾的字符串中的一些JSON,您可以使用cJSON_Parse解析它。

cJSON *json = cJSON_Parse(string);

    給定一個字符串中的一些JSON(無論是否終止爲0),您可以使用cJSON_ParseWithLength解析它。

cJSON *json = cJSON_ParseWithLength(string, buffer_length);

    它將解析JSON並分配一個表示它的cJSON項樹。一旦它返回,您將完全負責在與cJSON_Delete一起使用後對它進行釋放。


    cJSON_Parse使用的分配器默認是malloc和free,但是可以使用cJSON_InitHooks(全局)更改。

 

    如果發生錯誤,可以使用cJSON_GetErrorPtr訪問指向輸入字符串中錯誤位置的指針。注意,這可能會在多線程場景中產生競爭條件,在這種情況下,最好使用cJSON_ParseWithOpts和return_parse_end。默認情況下,解析後的JSON之後的輸入字符串中的字符不會被視爲錯誤。

 

    如果你想要更多的選項,使用cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_ended)。return_parse_end返回一個指針,指向輸入字符串中的JSON結尾或錯誤發生的位置(從而以線程安全的方式替換cJSON_GetErrorPtr)。require_null_ended,如果設置爲1,那麼如果輸入字符串包含JSON之後的數據,則會導致錯誤。


    如果你想要更多的設置緩衝區長度的選項,可以使用cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_)

輸出JSON

    給定一個cJSON項樹,您可以使用cJSON_Print將它們打印爲字符串。

char *string = cJSON_Print(json);

    它將分配一個字符串並將樹的JSON表示形式打印到其中。一旦它返回,您就完全有責任在與分配器一起使用後重新分配它。(通常是免費的,取決於cJSON_InitHooks設置了什麼)。
    

    cJSON_Print將使用空白來打印格式。如果您想打印沒有格式,使用cjson_printunformatting。

 

    如果您對結果字符串的大小有一個大致的概念,那麼您可以使用cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)。fmt是一個布爾值,用於打開和關閉空白格式。prebuffer指定用於打印的第一個緩衝區大小。cJSON_Print的第一個緩衝區大小是256字節。一旦打印耗盡空間,就會分配一個新的緩衝區,並在繼續打印之前複製舊的緩衝區。

 

    通過使用cjson_printpre(cJSON *item, char *buffer, const int length, const cJSON_bool格式)可以完全避免這些動態緩衝區分配。它接受一個緩衝區的指針打印到它的長度。如果達到該長度,打印將失敗並返回0。如果成功,則返回1。注意,您應該提供比實際需要更多的5個字節,因爲cJSON在估計所提供的內存是否足夠時不是100%準確的。

舉例

    在這個例子中,我們想要構建和解析以下JSON:

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

輸出

    讓我們構建上面的JSON並將其打印爲一個字符串:

//創建一個具有受支持的列表的監視器//注意:返回一個堆分配的字符串,您需要在使用後釋放它。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;    }    /* 創建成功後,立即將其添加到監視器中     * 從而轉移到它的指針的所有權 */    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;}

    或者,我們可以使用cJSON_Add…ToObject輔助功能,讓我們的生活更輕鬆:

//注意:返回一個堆分配的字符串,您需要在使用後釋放它。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;}

解析

    在這個例子中,我們將解析上述格式的JSON,並檢查監視器是否支持全高清分辨率,同時打印一些診斷輸出:

/* 如果監視器支持全高清,返回1,否則返回0 */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;}

    注意,除了cJSON_Parse的結果之外,沒有任何空檢查,因爲cJSON_GetObjectItemCaseSensitive已經檢查了空輸入,所以只傳播空值,如果輸入爲空,則cJSON_IsNumber和cJSON_IsString返回0。

警告

 

Zero Character零字符

    cJSON不支持包含0字符'\0'或\u0000的字符串。這在當前API中是不可能的,因爲字符串是零終止的。

Character Encoding字符編碼

    cJSON只支持UTF-8編碼的輸入。但在大多數情況下,它不會拒絕無效的UTF-8作爲輸入,只是按原樣傳播它。只要輸入不包含無效的UTF-8,輸出就始終是有效的UTF-8。

C StandardC標準

    cJSON是用ANSI C(或C89, C90)編寫的。如果編譯器或C庫不遵循這個標準,就不能保證正確的行爲。
    注意:ANSI C不是c++,所以它不應該用c++編譯器來編譯。不過,您可以使用C編譯器編譯它,並將它與您的c++代碼鏈接起來。雖然使用c++編譯器進行編譯可能有效,但不能保證正確的行爲。

Floating Point Numbers浮點數

    除了IEEE754雙精度浮點數外,cJSON不支持任何雙精度實現。它可能仍然可以與其他實現一起工作,但是其中的bug將被認爲是無效的。
    cJSON支持的浮點文字的最大長度目前是63個字符。

Deep Nesting Of Arrays And Objects數組和對象的深度嵌套

    cJSON不支持深度嵌套的數組和對象,因爲這會導致堆棧溢出。爲了防止這種情況,cJSON將深度限制爲CJSON_NESTING_LIMIT,默認值爲1000,但是可以在編譯時更改。

Thread Safety線程安全性

    一般來說,cJSON不是線程安全的。
    但在以下情況下是線程安全的:

  • cJSON_GetErrorPtr從未被使用過(可以使用cjson_parse_end參數cJSON_ParseWithOpts)

  • cJSON_InitHooks只在任何線程中使用cJSON之前被調用。

  • 在所有對cJSON函數的調用返回之前,從未調用setlocale。

Case Sensitivity大小寫敏感性

    在最初創建cJSON時,它沒有遵循JSON標準,也沒有區分大寫和小寫字母。如果您想要正確的、標準的兼容的行爲,您需要在可用的地方使用案例敏感函數。

Duplicate Object Members複製對象成員

    cJSON支持解析和打印包含具有多個同名成員的對象的JSON。然而,cJSON_GetObjectItemCaseSensitive總是隻返回第一個。

Enjoy cJSON!

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章