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,日志里的数据交互阅读起来简单舒服得多。