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。
<code>mkdir build cd build cmake ../<code>
這將創建一個Makefile和一堆其他文件。然後你可以編譯它:
<code>make/<code>
如果你想安裝的話,可以使用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,您可能會採取以下步驟:
<code>mkdir build cd build cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off -DCMAKE_INSTALL_PREFIX=/usr make make DESTDIR=$pkgdir install/<code>
在Windows上,CMake通常用於創建Visual Studio解決方案文件,方法是在Visual Studio的開發人員命令提示符中運行它,具體步驟遵循CMake和Microsoft的官方文檔,並使用您選擇的在線搜索引擎。上述選項的描述仍然普遍適用,儘管並非所有選項都適用於Windows。
Makefile
注意:不推薦使用此方法。儘可能使用CMake。Makefile支持僅限於修復bug。
如果你沒有可用的CMake,但仍然有GNU make。您可以使用makefile來構建cJSON: 在帶有源代碼的目錄中運行這個命令,它將自動編譯靜態和共享庫以及一個小測試程序(不是完整的測試套件)。
<code>make all/<code>
如果需要,可以使用make install將編譯後的庫安裝到系統中。默認情況下,它將在/usr/local/include/cjson中安裝標頭,在/usr/local/lib中安裝庫。但是您可以通過設置
PREFIX和DESTDIR變量來更改此行為:make PREFIX=/usr DESTDIR=temp install。然後使用:make PREFIX=/usr DESTDIR=temp uninstall來卸載它們。Vcpkg
你可以使用vcpkg依賴管理器下載和安裝cJSON:
<code>git clone https://github.com/Microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install vcpkg install cjson/<code>
vcpkg中的cJSON端口由Microsoft團隊成員和社區貢獻者保持最新。如果版本過期,請在vcpkg存儲庫中創建問題或拉出請求。
Including cJSON
如果你通過CMake或Makefile安裝它,你可以像這樣包含cJSON:
<code>#include /<code>
Data Structure
cJSON表示使用cJSON結構數據類型的JSON數據:
<code>/* 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;/<code>
這種類型的項表示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解析它。
<code>cJSON *json = cJSON_Parse(string);/<code>
給定一個字符串中的一些JSON(無論是否終止為0),您可以使用cJSON_ParseWithLength解析它。
<code>cJSON *json = cJSON_ParseWithLength(string, buffer_length);/<code>
它將解析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將它們打印為字符串。
<code>char *string = cJSON_Print(json);/<code>
它將分配一個字符串並將樹的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:
<code>{ "name": "Awesome 4K", "resolutions": [ { "width": 1280, "height": 720 }, { "width": 1920, "height": 1080 }, { "width": 3840, "height": 2160 } ] }/<code>
輸出
讓我們構建上面的JSON並將其打印為一個字符串:
<code>//創建一個具有受支持的列表的監視器 //注意:返回一個堆分配的字符串,您需要在使用後釋放它。 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; }/<code>
或者,我們可以使用cJSON_Add…ToObject輔助功能,讓我們的生活更輕鬆:
<code> //注意:返回一個堆分配的字符串,您需要在使用後釋放它。 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; }/<code>
解析
在這個例子中,我們將解析上述格式的JSON,並檢查監視器是否支持全高清分辨率,同時打印一些診斷輸出:
<code>/* 如果監視器支持全高清,返回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; }/<code>
注意,除了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總是隻返回第一個。