DaveGamble/cJSON

GitHub: DaveGamble/cJSON

一个用 ANSI C 编写的超轻量级 JSON 解析器,仅含一个 C 文件和一个头文件,提供简洁的 JSON 解析和生成功能。

Stars: 12496 | Forks: 3447

# cJSON ANSI C 编写的超轻量级 JSON 解析器。 ## 目录 * [许可证](#license) * [用法](#usage) * [欢迎使用 cJSON](#welcome-to-cjson) * [构建](#building) * [复制源码](#copying-the-source) * [CMake](#cmake) * [Makefile](#makefile) * [Meson](#meson) * [Vcpkg](#Vcpkg) * [包含 cJSON](#including-cjson) * [数据结构](#data-structure) * [操作数据结构](#working-with-the-data-structure) * [基本类型](#basic-types) * [数组](#arrays) * [对象](#objects) * [解析 JSON](#parsing-json) * [打印 JSON](#printing-json) * [示例](#example) * [打印](#printing) * [解析](#parsing) * [注意事项](#caveats) * [零字符](#zero-character) * [字符编码](#character-encoding) * [C 标准](#c-standard) * [浮点数](#floating-point-numbers) * [数组和对象的深度嵌套](#deep-nesting-of-arrays-and-objects) * [线程安全](#thread-safety) * [大小写敏感性](#case-sensitivity) * [重复的对象成员](#duplicate-object-members) * [享受 cJSON!](#enjoy-cjson) ## 许可证 MIT 许可证 ## 用法 ### 欢迎使用 cJSON cJSON 的目标是成为能完成任务的最简单的解析器。 它只包含一个 C 文件和一个头文件。 JSON 最好在这里描述:http://www.json.org/ 它就像 XML,但没有多余的脂肪。你用它来移动数据、存储东西,或者只是通常用来表示程序的状态。 作为一个库,cJSON 的存在是为了尽可能多地承担繁琐的工作,但又不妨碍你。 出于实用主义(即忽略事实),我会说你可以在两种模式之一使用它:自动和手动。让我们快速过一遍。 我从这个页面提取了一些 JSON:http://www.json.org/fatfree.html 那个页面启发我编写了 cJSON,这是一个试图与 JSON 本身分享相同理念的解析器。简单、笨拙、不碍事。 ### 构建 有几种方法可以将 cJSON 纳入你的项目。 #### 复制源码 因为整个库只有一个 C 文件和一个头文件,你可以直接将 `cJSON.h` 和 `cJSON.c` 复制到你的项目源码中并开始使用它。 cJSON 是用 ANSI C (C89) 编写的,以便支持尽可能多的平台和编译器。 #### CMake 使用 CMake,cJSON 支持完整的构建系统。这样你可以获得最多的功能。支持 2.8.5 或更高版本的 CMake。使用 CMake 时,建议进行 out of tree 构建,这意味着编译后的文件放在与源文件分开的目录中。因此,为了在 Unix 平台上使用 CMake 构建 cJSON,请创建一个 `build` 目录并在其中运行 CMake。 ``` mkdir build cd build cmake .. ``` 这将创建一个 Makefile 和一堆其他文件。然后你可以编译它: ``` make ``` 如果你愿意,可以用 `make install` 安装它。默认情况下,它将头文件安装到 `/usr/local/include/cjson`,库安装到 `/usr/local/lib`。它还为 pkg-config 安装文件,以便更容易检测和使用现有的 CMake 安装。并且它安装 CMake 配置文件,其他基于 CMake 的项目可以使用这些文件来发现该库。 你可以通过传递给 CMake 的一系列不同选项来更改构建过程。用 `On` 开启,用 `Off` 关闭: * `-DENABLE_CJSON_TEST=On`:启用构建测试。(默认开启) * `-DENABLE_CJSON_UTILS=On`:启用构建 cJSON_Utils。(默认关闭) * `-DENABLE_TARGET_EXPORT=On`:启用 CMake targets 的导出。如果出现问题请关闭。(默认开启) * `-DENABLE_CUSTOM_COMPILER_FLAGS=On`:启用自定义编译器标志(目前用于 Clang、GCC 和 MSVC)。如果出现问题请关闭。(默认开启) * `-DENABLE_VALGRIND=On`:使用 [valgrind](http://valgrind.org) 运行测试。(默认关闭) * `-DENABLE_SANITIZERS=On`:编译 cJSON 时启用 [AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) 和 [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)(如果可能)。(默认关闭) * `-DENABLE_SAFE_STACK`:启用 [SafeStack](https://clang.llvm.org/docs/SafeStack.html) 检测过程。目前仅适用于 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_SHARED_LIBS` 覆盖 `BUILD_SHARED_LIBS` 的值。 * `-DENABLE_CJSON_VERSION_SO`:启用 cJSON so 版本。(默认开启) 如果你正在为某个 Linux 发行版打包 cJSON,你可能会采取以下步骤: ``` mkdir build cd build cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off -DCMAKE_INSTALL_PREFIX=/usr make make DESTDIR=$pkgdir install ``` 在 Windows 上,CMake 通常用于通过在 Visual Studio 的 Developer Command Prompt 中运行来创建 Visual Studio 解决方案文件,具体步骤请遵循 CMake 和 Microsoft 的官方文档,并使用你选择的在线搜索引擎。上述选项的描述通常仍然适用,尽管并非所有选项都在 Windows 上有效。 #### Makefile **注意:** 此方法已弃用。如果可能,请使用 CMake。Makefile 支持仅限于修复错误。 如果你没有 CMake,但仍然有 GNU make。你可以使用 makefile 构建 cJSON: 在包含源代码的目录中运行此命令,它将自动编译静态和共享库以及一个小测试程序(不是完整的测试套件)。 ``` make all ``` 如果你愿意,你可以使用 `make install` 将编译好的库安装到你的系统。默认情况下,它会将头文件安装在 `/usr/local/include/cjson`,库安装在 `/usr/local/lib`。但你可以通过设置 `PREFIX` 和 `DESTDIR` 变量来更改此行为:`make PREFIX=/usr DESTDIR=temp install`。并使用以下命令卸载:`make PREFIX=/usr DESTDIR=temp uninstall`。 #### Meson 要使 cjson 在使用 meson 的项目中工作,必须包含 libcjson 依赖: ``` project('c-json-example', 'c') cjson = dependency('libcjson') example = executable( 'example', 'example.c', dependencies: [cjson], ) ``` #### Vcpkg 你可以使用 [vcpkg](https://github.com/Microsoft/vcpkg) 依赖管理器下载并安装 cJSON: ``` git clone https://github.com/Microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install vcpkg install cjson ``` vcpkg 中的 cJSON 端口由 Microsoft 团队成员和社区贡献者保持最新。如果版本已过时,请在 vcpkg 仓库上[创建 issue 或 pull request](https://github.com/Microsoft/vcpkg)。 ### 包含 cJSON 如果你通过 CMake 或 Makefile 安装了它,你可以像这样包含 cJSON: ``` #include ``` ### 数据结构 cJSON 使用 `cJSON` 结构体数据类型来表示 JSON 数据: ``` /* The cJSON structure: */ 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` 检查):表示 `false` 布尔值。你也可以使用 `cJSON_IsBool` 检查一般的布尔值。 * `cJSON_True`(用 `cJSON_IsTrue` 检查):表示 `true` 布尔值。你也可以使用 `cJSON_IsBool` 检查一般的布尔值。 * `cJSON_NULL`(用 `cJSON_IsNull` 检查):表示 `null` 值。 * `cJSON_Number`(用 `cJSON_IsNumber` 检查):表示数字值。该值在 `valuedouble` 中存储为 double,也在 `valueint` 中存储。如果数字超出整数范围,则 `valueint` 使用 `INT_MAX` 或 `INT_MIN`。 * `cJSON_String`(用 `cJSON_IsString` 检查):表示字符串值。它以零终止字符串的形式存储在 `valuestring` 中。 * `cJSON_Array`(用 `cJSON_IsArray` 检查):表示数组值。这是通过将 `child` 指向表示数组中值的 `cJSON` 项的链表来实现的。元素使用 `next` 和 `prev` 链接在一起,其中第一个元素的 `prev.next == NULL`,最后一个元素的 `next == NULL`。 * `cJSON_Object`(用 `cJSON_IsObject` 检查):表示对象值。对象的存储方式与数组相同,唯一的区别是对象中的项将其键存储在 `string` 中。 * `cJSON_Raw`(用 `cJSON_IsRaw` 检查):表示任何以零终止字符数组形式存储在 `valuestring` 中的 JSON 类型。例如,这可用于避免一遍又一遍地打印相同的静态 JSON 以节省性能。cJSON 在解析时永远不会创建此类型。另请注意,cJSON 不检查它是否是有效的 JSON。 此外,还有以下两个标志: * `cJSON_IsReference`:指定 `child` 指向的项和/或 `valuestring` 不由此项拥有,它只是一个引用。因此 `cJSON_Delete` 和其他函数只会释放此项,而不会释放其 `child`/`valuestring`。 * `cJSON_StringIsConst`:这意味着 `string` 指向一个常量字符串。这意味着 `cJSON_Delete` 和其他函数不会尝试释放 `string`。 ### 操作数据结构 对于每种值类型,都有一个 `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`。如果数字超出整数范围,则 `valueint` 使用 `INT_MAX` 或 `INT_MIN` * **strings** 使用 `cJSON_CreateString`(复制字符串)或 `cJSON_CreateStringReference`(直接指向字符串。这意味着 `valuestring` 不会被 `cJSON_Delete` 删除,你需要负责其生命周期,对常量有用)创建 #### 数组 你可以使用 `cJSON_CreateArray` 创建一个空数组。`cJSON_CreateArrayReference` 可用于创建一个不“拥有”其内容的数组,因此其内容不会被 `cJSON_Delete` 删除。 要将项添加到数组,请使用 `cJSON_AddItemToArray` 将项追加到末尾。 使用 `cJSON_AddItemReferenceToArray` 可以将元素作为对另一个项、数组或字符串的引用添加。这意味着 `cJSON_Delete` 不会删除该项的 `child` 或 `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` 结构体中的 `string`)的对象,以便它不会被 `cJSON_Delete` 释放。 使用 `cJSON_AddItemReferenceToArray` 可以将元素作为对另一个对象、数组或字符串的引用添加。这意味着 `cJSON_Delete` 不会删除该项的 `child` 或 `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(无论是否为零终止),你可以使用 `cJSON_ParseWithLength` 解析它。 ``` cJSON *json = cJSON_ParseWithLength(string, buffer_length); ``` 它将解析 JSON 并分配一个表示它的 `cJSON` 项树。一旦它返回,你将全权负责在使用后使用 `cJSON_Delete` 释放它。 默认情况下,`cJSON_Parse` 使用的分配器是 `malloc` 和 `free`,但可以使用 `cJSON_InitHooks` 更改(全局)。 如果发生错误,可以使用 `cJSON_GetErrorPtr` 访问指向输入字符串中错误位置的指针。但请注意,这可能会在多线程场景中产生竞争条件,在这种情况下,最好使用带有 `return_parse_end` 的 `cJSON_ParseWithOpts`。 默认情况下,输入字符串中紧跟已解析 JSON 的字符不会被视为错误。 如果你想要更多选项,请使用 `cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)`。 `return_parse_end` 返回指向输入字符串中 JSON 末尾的指针或发生错误的位置(从而以线程安全的方式替换 `cJSON_GetErrorPtr`)。如果设置为 `1`,`require_null_terminated` 将使输入字符串在 JSON 之后包含数据成为错误。 如果你想要更多指定缓冲区长度的选项,请使用 `cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)`。 ### 打印 JSON 给定一个 `cJSON` 项树,你可以使用 `cJSON_Print` 将它们打印为字符串。 ``` char *string = cJSON_Print(json); ``` 它将分配一个字符串并将树的 JSON 表示形式打印到其中。一旦它返回,你将全权负责在使用后使用你的分配器释放它。(通常是 `free`,取决于 `cJSON_InitHooks` 设置的内容)。 `cJSON_Print` 将打印带有用于格式化的空白。如果你想在不格式化的情况下打印,请使用 `cJSON_PrintUnformatted`。 如果你对结果字符串的大小有一个粗略的估计,你可以使用 `cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)`。`fmt` 是一个布尔值,用于开启和关闭带空白的格式化。`prebuffer` 指定用于打印的第一个缓冲区大小。`cJSON_Print` 当前使用 256 字节作为其第一个缓冲区大小。一旦打印空间用完,就会分配一个新的缓冲区,并在继续打印之前复制旧缓冲区。 通过使用 `cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)`,可以完全避免这些动态缓冲区分配。它接受一个要打印到的缓冲区指针及其长度。如果达到长度,打印将失败并返回 `0`。如果成功,则返回 `1`。请注意,你应该提供比实际需要多 5 个字节,因为 cJSON 在估计提供的内存是否足够时不是 100% 准确。 ### 示例 在这个例子中,我们要构建并解析以下 JSON: ``` { "name": "Awesome 4K", "resolutions": [ { "width": 1280, "height": 720 }, { "width": 1920, "height": 1080 }, { "width": 3840, "height": 2160 } ] } ``` #### 打印 让我们构建上面的 JSON 并将其打印到字符串: ``` //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; } ``` 或者我们可以使用 `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; } ``` #### 解析 在这个例子中,我们将解析上述格式的 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; } ``` 请注意,除了 `cJSON_Parse` 的结果外,没有 NULL 检查,因为 `cJSON_GetObjectItemCaseSensitive` 已经检查了 `NULL` 输入,因此 `NULL` 值只是被传播,如果输入为 `NULL`,`cJSON_IsNumber` 和 `cJSON_IsString` 返回 `0`。 ### 注意事项 #### 零字符 cJSON 不支持包含零字符 `'\0'` 或 `\u0000` 的字符串。这对于当前 API 是不可能的,因为字符串是零终止的。 #### 字符编码 cJSON 仅支持 UTF-8 编码的输入。在大多数情况下,它不会拒绝无效的 UTF-8 作为输入,它只是按原样传播它。只要输入不包含无效的 UTF-8,输出将始终是有效的 UTF-8。 #### C 标准 cJSON 是用 ANSI C(或 C89、C90)编写的。如果你的编译器或 C 库不遵循此标准,则无法保证正确的行为。 注意:ANSI C 不是 C++,因此不应使用 C++ 编译器编译它。但是,你可以使用 C 编译器编译它并将其与你的 C++ 代码链接。尽管使用 C++ 编译器编译可能有效,但不能保证正确的行为。 #### 浮点数 cJSON 官方不支持 IEEE754 双精度浮点数以外的任何 `double` 实现。它可能仍然适用于其他实现,但这些错误将被视为无效。 cJSON 支持的浮点字面量的最大长度当前为 63 个字符。 #### 数组和对象的深度嵌套 cJSON 不支持嵌套过深的数组和对象,因为这会导致堆栈溢出。为了防止这种情况,cJSON 将深度限制为 `CJSON_NESTING_LIMIT`,默认为 1000,但可以在编译时更改。 #### 线程安全 通常,cJSON **不是线程安全的**。 但是,在以下条件下它是线程安全的: * 从不使用 `cJSON_GetErrorPtr`(可以改用 `cJSON_ParseWithOpts` 的 `return_parse_end` 参数) * `cJSON_InitHooks` 仅在任何线程中使用 cJSON 之前调用。 * 在所有对 cJSON 函数的调用返回之前,从未调用 `setlocale`。 #### 大小写敏感性 当最初创建 cJSON 时,它不遵循 JSON 标准,并且不区分大写和小写字母。如果你想要正确的、符合标准的行为,则需要在可用的情况下使用 `CaseSensitive` 函数。 #### 重复的对象成员 cJSON 支持解析和打印包含具有多个同名成员的对象的 JSON。然而 `cJSON_GetObjectItemCaseSensitive` 将始终只返回第一个。 # 享受 cJSON! - Dave Gamble(原作者) - Max Bruckner 和 Alan Wang(现任维护者) - 以及其他 [cJSON 贡献者](CONTRIBUTORS.md)
标签:ANSI C, Bash脚本, CMake, HTTP 参数枚举, IoT, JSON解析器, Meson, Vcpkg, 单文件库, 反序列化, 后端开发, 字符编码, 客户端加密, 嵌入式开发, 序列化, 开源库, 搜索引擎爬虫, 数据结构, 解析库, 超轻量级, 键值对存储, 预握手