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

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!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200407A0THZC00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券