cJSON使用介绍以及如何利用cJSON在服务端和客户端中进行数据传输

2024-07-27 15:05:58 浏览数 (2)

cJSON简介:一种高效且易于使用的 JSON 解析器和生成器

在当今的软件开发领域中,JSON(JavaScript Object Notation)已成为数据交换的标准格式之一。由于其简洁、易读和跨平台的特性,它被广泛应用于Web应用程序、移动应用和物联网设备中。对于需要在C语言环境中处理JSON的应用程序而言,cJSON 是一个非常实用且流行的库。

如何使用cJSON进行数据交互?

首先通过github地址

https://github.com/DaveGamble/cJSON

将其中的

下载至想要引入cJSON的项目里就行

如图所示

cJSON介绍:

创建 JSON 数据:

代码语言:c复制
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);

cJSON_CreateNull(): 创建一个 null 类型的 JSON 对象。

cJSON_CreateTrue() / cJSON_CreateFalse(): 创建一个布尔值类型的 JSON 对象。

cJSON_CreateNumber(double num): 创建一个数字类型的 JSON 对象。

cJSON_CreateString(const char *str): 创建一个字符串类型的 JSON 对象。

cJSON_CreateArray(): 创建一个 JSON 数组。

cJSON_CreateObject(): 创建一个 JSON 对象。

添加数据

这里只介绍cJSON_AddItemToArray和cJSON_AddItemToObject以及其宏定义相关的简单使用例

代码语言:c复制
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void	cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
extern void	cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item);	/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);

/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b)	cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n)	cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s)	cJSON_AddItemToObject(object, name, cJSON_CreateString(s))

cJSON_AddItemToArray

添加数组元素到数组

将 item 添加到 array 中。item 可以是任意类型的 cJSON 结构体(字符串、数字、对象、数组等)。

cJSON_AddItemReferenceToArray

将 item 的引用添加到 array 中。与 cJSON_AddItemToArray 不同的是,这里不复制 item,而是直接引用它,这就意味着cJSON_Delete不会去删除这个元素的child或者valuestring属性,因此当这些属性在其他地方使用的时候,不用担心重复释放内存的事情发生。

AddItemToArray使用例子

代码语言:cpp复制
	cJSON* alphabet = cJSON_CreateArray();
	cJSON_AddItemToArray(alphabet, cJSON_CreateString("A"));
	cJSON_AddItemToArray(alphabet, cJSON_CreateString("B"));
    cJSON_AddItemToArray(alphabet, cJSON_CreateString("C"));
	const char* strAlphabet = cJSON_Print(alphabet);

	std::cout << strAlphabet <<std::endl;

cJSON_AddItemToObject

将 item 任意类型的 cJSON 结构体(字符串、数字、对象、数组等)。作为键 string 的值添加到 object 中。

cJSON_AddItemToObjectCS

与 cJSON_AddItemToObject 类似,但这个函数假设 string 是常量且其生命周期至少与 item 一样长。这意味着 string 字符串不会被 cJSON 库复制,而是直接引用。

cJSON_AddItemReferenceToObject

将 item 的引用作为键 string 的值添加到 object 中。与 cJSON_AddItemToObject 不同的是,这里不复制 item,而是直接引用它。

cJSON_AddItemToObject嵌套对象的简单使用例

代码语言:cpp复制
	// 创建 JSON 数据
	cJSON* json = cJSON_CreateObject();
	cJSON_AddStringToObject(json, "name", "Yzc");
	cJSON_AddNumberToObject(json, "age", 18);
    //创造嵌套对象
	cJSON* address = cJSON_CreateObject();
	cJSON_AddStringToObject(address, "street", "changsha");
	cJSON_AddNumberToObject(address, "zip", 10086);
	//嵌套对象将address赋给json
	cJSON_AddItemToObject(json, "address", address);

使用宏定义添加数据的简单使用例:

代码语言:cpp复制
	cJSON* root = cJSON_CreateObject();
	const char* maxNumber = "18";
	cJSON_AddStringToObject(root, "maxNumber", maxNumber);
	cJSON_AddNumberToObject(root, "result", 1);
	cJSON_AddNullToObject(root, "nullValue");
	cJSON_AddTrueToObject(root, "isActive");
	cJSON_AddFalseToObject(root, "isInactive");
	cJSON_AddBoolToObject(root, "isEnabled", true);
	char* rootShow = cJSON_Print(root);

获取/解析 JSON 数据

获取JSON数据相关

代码语言:cpp复制
/* Get Array size/item / object item. */
int    cJSON_GetArraySize(cJSON *array)							
{cJSON *c=array->child;int i=0;while(c)i  ,c=c->next;return i;}
cJSON *cJSON_GetArrayItem(cJSON *array,int item)				
{cJSON *c=array->child;  while (c && item>0) item--,c=c->next; return c;}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string)	
{cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}

cJSON_GetObjectItem

返回 object 中键名为 string 的元素。

cJSON_GetObjectItem相关使用例

注意:cJSON_GetObjectItem(root, "number")->valuestring返回的实际上是char* 类型

假设root里有 key 为 Id rootName number 的键值对

代码语言:cpp复制
    cJSON* root = cJSON_CreateObject():
	int Id = cJSON_GetObjectItem(root, "Id")->valueint;
	const char* rootName = cJSON_GetObjectItem(root, "rootName")->valuestring;
    cJSON* cJSONNumberTest = cJSON_GetObjectItem(root, "number")
    int number = cJSONNumberTest ->valueint;
    std::string str1111(cJSON_GetObjectItem(root, "number")->valuestring);

cJSON_GetArrayItem

返回 array 中位于索引 item 的元素。

cJSON_GetArraySize

计算并返回 array 中的元素个数。

遍历 JSON 数据的一个简单示例

注意 :这里的alphabet 是cJSON_Array类型

代码语言:cpp复制
	if (alphabet->type == cJSON_Array) {
		int arraySize = cJSON_GetArraySize(alphabet);
		for (int i = 0; i < arraySize; i  ) {
			cJSON* item = cJSON_GetArrayItem(alphabet, i);
			if (item->type == cJSON_String) {
				std::cout << "Element " << i << ": " << item->valuestring << std::endl;
			}
		}
	}

解析相关

cJSON cJSON_Parse(const char value) 从字符串中解析 JSON 数据。

一个简单的使用例

代码语言:cpp复制
	// 创建 JSON 数据
	cJSON* json = cJSON_CreateObject();
    //~~~略过相应的数据创造和解析相关,参考简单的使用例即可
	// 解析 JSON 数据
	cJSON* parsed_json = cJSON_Parse(json_string);

cJSON cJSON_ParseWithOpts(const char value, size_t length, int require_null_terminated)

cJSON_ParseWithOpts的解析及其简单使用例:

value:指向待解析 JSON 字符串的常量指针。

return_parse_end: 指向一个 const char * 的指针。如果非 NULL,函数会将解析结束位置的指针存储在这里。这有助于调试和验证解析是否成功到达了字符串的末尾。

require_null_terminated: 一个布尔值(实际上是一个整数),用来指示是否要求 JSON 字符串以空字符 () 终止。如果设置为 1,则要求字符串以 结尾;如果设置为 0,则不作此要求。

返回值

如果成功解析 JSON 字符串,返回指向 cJSON 结构体的指针。

如果解析失败,返回 NULL。可以通过 cJSON_GetErrorPtr() 获取失败原因。

代码语言:cpp复制
    // 创建 JSON 字符串
    const char *json_string = "{"name":"John","age":30}";

    // 解析 JSON 数据
    const char *end_of_parsed;
    cJSON *parsed_json = cJSON_ParseWithOpts(json_string, &end_of_parsed, 1);

    if (parsed_json) {
        // 解析成功
        printf("Parsed successfully.n");
        printf("End of parsing: '%s'n", end_of_parsed);

        // 获取 "name" 键的值
        cJSON *name_item = cJSON_GetObjectItem(parsed_json, "name");
        if (name_item && name_item->type == cJSON_String) {
            printf("Name: %sn", name_item->valuestring);
        }

        // 获取 "age" 键的值
        cJSON *age_item = cJSON_GetObjectItem(parsed_json, "age");
        if (age_item && age_item->type == cJSON_Number) {
            printf("Age: %dn", age_item->valueint);
        }

        // 清理 JSON 数据
        cJSON_Delete(parsed_json);
    } else {
        // 解析失败
        printf("Failed to parse JSON data: %sn", cJSON_GetErrorPtr());
    }

获取 JSON 数据类型

这里的json 是cJSON* 类型

int jsonType = json->type;

对应的jsonType

代码语言:cpp复制
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6

转换 JSON 数据(CS交互常用)

char *cJSON_Print(cJSON *value): 将 JSON 数据转换为字符串(默认格式化输出)。

char *cJSON_PrintUnformatted(cJSON *value): 将 JSON 数据转换为字符串(不格式化输出)。

释放内存

void cJSON_Delete(cJSON *c): 删除 JSON 对象及其所有子项,并释放关联的内存。

和在一起的一个cJSONapi相关简单使用例

代码语言:cpp复制
#include <iostream>
#include "cJSON.h"

int main() {
	// 创建 JSON 数据
	cJSON* JsonTest = cJSON_CreateObject();
	cJSON_AddStringToObject(JsonTest, "name", "Yzc");
	cJSON_AddNumberToObject(JsonTest, "age", 18);
	//嵌套对象cJSON
	cJSON* address = cJSON_CreateObject();
	cJSON_AddStringToObject(address, "street", "changsha");
	cJSON_AddNumberToObject(address, "zip", 10086);
	cJSON_AddItemToObject(JsonTest, "address", address);

	// 打印 JSON 数据
	std::string json_string(cJSON_Print(JsonTest));
	std::cout << "JSON Data:n" << json_string << std::endl;
	// 解析 JSON 数据示例
	cJSON* parsed_json = cJSON_Parse(json_string.c_str());
	if (parsed_json) {
		// 获取 "name" 键的值
		cJSON* name_item = cJSON_GetObjectItem(parsed_json, "name");
		//std::string abc = name_item->valuestring;
		//std::cout << "abc:" << abc << std::endl;
		if (name_item && name_item->type == cJSON_String) {
			std::cout << "Name:" << name_item->valuestring << std::endl;
		}

		// 获取 "age" 键的值
		cJSON* age_item = cJSON_GetObjectItem(parsed_json, "age");
		if (age_item && age_item->type == cJSON_Number) {
			std::cout << "Age:" << age_item->valueint << std::endl;
		}

		// 嵌套对象示例
		cJSON* address_item = cJSON_GetObjectItem(parsed_json, "address");
		if (address_item && address_item->type == cJSON_Object) {
			cJSON* street_item = cJSON_GetObjectItem(address_item, "street");
			if (street_item && street_item->type == cJSON_String) {
				std::cout << "Street:" << street_item->valuestring << std::endl;
			}
			cJSON* zip_item = cJSON_GetObjectItem(address_item, "zip");
			if (zip_item && zip_item->type == cJSON_Number) {
				std::cout << "Zip:" << zip_item->valueint << std::endl;
			}
		}

		// 清理 解析的JSON 数据
		cJSON_Delete(parsed_json);
	}
	else {
		// 解析失败时的错误处理
		std::cout <<"Failed to parse JSON data: %snn" << cJSON_GetErrorPtr() << std::endl;
	}
	//非格式化输出
	std::string cJSON_Show(cJSON_PrintUnformatted(JsonTest));

	std::cout << "ncJSON_Show:" << cJSON_Show << std::endl;

	cJSON* alphabet = cJSON_CreateArray();

	cJSON_AddItemToArray(alphabet, cJSON_CreateString("A"));
	cJSON_AddItemToArray(alphabet, cJSON_CreateString("B"));
	cJSON_AddItemToArray(alphabet, cJSON_CreateString("C"));

	if (alphabet->type == cJSON_Array) {
		int arraySize = cJSON_GetArraySize(alphabet);
		for (int i = 0; i < arraySize; i  ) {
			cJSON* item = cJSON_GetArrayItem(alphabet, i);
			if (item->type == cJSON_String) {
				std::cout << "Element " << i << ": " << item->valuestring << std::endl;
			}
		}
	}
	const char* strAlphabet =cJSON_Print(alphabet);

	std::cout << "nstrAlphabet:" << strAlphabet << std::endl;

    //添加数据示例
	cJSON* root = cJSON_CreateObject();
	const char* maxNumber = "18";

	cJSON_AddStringToObject(root, "maxNumber", maxNumber);
	cJSON_AddNumberToObject(root, "result", 1);
	cJSON_AddNullToObject(root, "nullValue");
	cJSON_AddTrueToObject(root, "isActive");
	cJSON_AddFalseToObject(root, "isInactive");
	cJSON_AddBoolToObject(root, "isEnabled", true);
	std::string rootShow(cJSON_Print(root));

	std::cout << "nrootShow:" << rootShow << std::endl;

	// 清理
	cJSON_Delete(JsonTest);
	//cJSON_Delete(address);
	//address 是嵌套在 JsonTest 上的
	//之所以不需要这个Delete了 是因为cJSON_Delete(JsonTest)会把所有衍伸得cJSON对象都删除
	cJSON_Delete(alphabet);
	cJSON_Delete(root);

	return 0;
}

运行截图:

CS数据传输以一个简单的注册信息交互为例

略去服务端客户端的搭建,这里仅以数据交互为例

发送或接收时:

首先声明一个cJSON* 的root对象用于存储信息

将内容通过键值对的方式绑定到root对象以后

将其转换字符串,格式化或者非格式化都行,非格式化会节约字符串内存,格式化易于阅读

然后通过CS交互的信息传递如recv或者send函数进行交互

最后记得删除cJSON* 对象

客户端

发送注册消息

代码语言:cpp复制
void SendRegisterRequest(const char* nickname,const char* password)
{
	cJSON* root = cJSON_CreateObject();
	cJSON_AddStringToObject(root, "cmd", "CS_Register");
	cJSON_AddStringToObject(root, "nickname", nickname);
	cJSON_AddStringToObject(root, "password", password);

	char* data = cJSON_PrintUnformatted(root);
	int len = strlen(data);

	send(sockClient, data, len, 0);

	cJSON_Delete(root);
	free(data);
}

接收注册信息

代码语言:cpp复制
int GameClient::OnNetMsg(const char* buf, int len)
{
	std::string msg(buf, len);
	printf("Client recv<-----%sn", msg.c_str());

	cJSON* root = cJSON_Parse(data);

	if (root == nullptr) return size;

	cJSON* cmd = cJSON_GetObjectItem(root, "cmd");

	if (strcmp("SC_Register", cmd->valuestring) == 0)
	{
		cJSON* msg = cJSON_GetObjectItem(root, "msg");
		if (strcmp(msg->valuestring, "Register Success") == 0)
		{
			std::cout << "Register successful!" << std::endl;
			CS_RegisterEvent(root);
		}
		else std::cout << "Client Register Failed,User isExitence" << std::endl;
	}
	else {
		printf("未知命令 [%s]n", cmd->valuestring);
	}

	cJSON_Delete(root);
	return size;
}

服务端

接收注册请求消息

代码语言:cpp复制
int TcpSocket::OnNetMsg(const char* buf, int len)
{
	std::string msg(buf , size);

	printf("Server recv<-----%sn", msg.c_str());

	cJSON* root = cJSON_Parse(msg.c_str());

	cJSON* cmd = cJSON_GetObjectItem(root, "cmd");

	if (cmd == nullptr)
	{
		cJSON_Delete(root);
		return size;
	}

	if (cmd->type != cJSON_String)
	{
		Close();
		cJSON_Delete(root);
		return size;
	}
	if (strcmp("CS_Register", cmd->valuestring) == 0)
		SC_RegisterRespond(root);
	else
		Close();

	cJSON_Delete(root);
	return size;
}

发送注册回复消息

代码语言:cpp复制
void TcpSocket::SC_RegisterRespond(cJSON* root)
{
	cJSON* nickname = cJSON_GetObjectItem(root, "nickname");
	cJSON* password = cJSON_GetObjectItem(root, "password");

	if (nickname == nullptr || nickname->type != cJSON_String)
	{
		Close();
		return;
	}

	cJSON* sc_register = cJSON_CreateObject();

	// 随机生成 不重复8位字符串
	std::string uniqueString = generateUniqueRandomString(8);
	std::cout << "Generated unique random string: " << uniqueString << std::endl;

	cJSON_AddStringToObject(sc_register, "cmd", "SC_Register");
	cJSON_AddStringToObject(sc_register, "token", uniqueString.c_str());
	cJSON_AddStringToObject(sc_register, "nickname", nickname->valuestring);
	cJSON_AddStringToObject(sc_register, "password", password->valuestring);
	cJSON_AddNumberToObject(sc_register, "result", 1);
	cJSON_AddStringToObject(sc_register, "msg", "Register Success");
	}

	char* msg = cJSON_Print(sc_register);

	cJSON_Delete(sc_register);

	SendData(msg, strlen(msg));

	free(msg);

}

截图展示

总结

JSON 格式简单明了,易于理解和编写,而且api调用简单易于理解和阅读。

数据传输格式用JSON,日志里的数据交互阅读起来简单舒服得多。

0 人点赞