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, 单文件库, 反序列化, 后端开发, 字符编码, 客户端加密, 嵌入式开发, 序列化, 开源库, 搜索引擎爬虫, 数据结构, 解析库, 超轻量级, 键值对存储, 预握手