IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言实现JSON字符串解码与编码(三)源代码 -> 正文阅读

[C++知识库]C语言实现JSON字符串解码与编码(三)源代码

申请/释放空间

因为JSON对象与字符串需要在堆上申请/释放空间,故定义2个函数指针变量来存在函数地址。

static void* (*JSON_malloc)(size_t _Size);//申请空间的函数指针
static void (*JSON_free)(void* _Block);//释放空间的函数指针

通过调用JSONInit()函数来初始化这两个函数指针

/** \brief  JSON初始化
 *
 * \param   JSONmalloc 申请空间函数的地址
 * \param   JSONfree 释放空间函数的地址
 * \return  无
 *
 */
void JSONInit(void* (*JSONmalloc)(size_t _Size), void (*JSONfree)(void* _Block))
{
	JSON_malloc = JSONmalloc;
	JSON_free = JSONfree;
}

解码

因为功能函数过多,这里仅介绍几个重要的函数。

解码JSON

传入需要解码的JSON字符串,与对应的数据模型,即可解码。

  1. 检查参数
  2. 检查申请/释放空间函数指针是否已经赋值
  3. 根对象是否为空
  4. 调用解析对象功能函数开始解析JSON字符串
/** \brief  解码JSON字符串
 *
 * \param   jsonStr JSON字符串
 * \param   model JSON字符串对应的对象的数据模型
 * \param   modelCount 对象的数据模型数量
 * \param   removeInvalidChars 是否去除 jsonStr 中的无效字符(空格 换行 回车)
 * \return  解码成功则返回解析得到的对象的地址
 *				解码失败则返回 NULL 可通过 JSONErrorCode() 查看错误代码
 *
 * \note	使用完记得调用FreeJSONObject释放对象申请的空间
 *				此函数会破坏 jsonStr 字符串的完整性(会将字符串中的字符(,)替换成字符(\0))
 */
void* DecodeJSON(char* jsonStr, PJSON_MODEL_t model, unsigned int modelCount, char removeInvalidChars)
{
	void* root = NULL;

	/* 复位错误标志 */
	JSONErrorCode = JSON_SUCCESS;

	/* 剔除字符串为空的情况 */
	if (jsonStr == NULL || model == NULL || modelCount == 0)
	{
		JSONErrorCode = PARAM_IS_EMPTY;//参数为空失败
		return NULL;
	}

	if (JSON_malloc == NULL || JSON_free == NULL)
	{
		JSONErrorCode = JSON_NOT_INIT;//未初始化
		return NULL;
	}

	if (removeInvalidChars)
	{
		/* 去除无效字符 */
		strTrim(jsonStr);
	}

#if (JSON_DEBUG == 1)
	printf("解码的JSON字符串长度为:%zu\n", strlen(jsonStr));
	printf("解码的JSON字符串为:%s\n", jsonStr);
#endif // (JSON_DEBUG == 1)

	/* 检查根对象是不是空对象 */
	if (*(jsonStr + 1) == '}')
	{
		JSONErrorCode = DECODE_ROOT_OBJECT_NULL;//根对象是不是空对象
		return NULL;
	}

	/* 解析数据 */
	root = DecodeObject(jsonStr, model, modelCount);

	return root;
}

解析对象

  1. 检查函数参数
  2. 检查对象为空
  3. 获取根对象占用的字节大小
  4. 为对象申请空间
  5. 变量对象的数据模型,依次解析JSON字符串
  6. 检查错误代码,出错则释放对象,函数返回NULL
/** \brief  解码JSON字符串中的对象
 *
 * \param   jsonStr 对象的JSON字符串(JSON字符串不能包含对象的键名于字符 : )
 * \param   model JSON字符串对应的对象的数据模型
 * \param   modelCount 对象的数据模型数量
 * \return  解码成功则返回解析得到的对象的地址
 *				解码失败则返回 NULL 可通过 JSONErrorCode() 查看错误代码
 *
 */
static void* DecodeObject(const char* jsonStr, PJSON_MODEL_t model, unsigned int modelCount)
{
	void* obj = NULL;
	unsigned short objectSize = 0;
	const char* value = jsonStr;
	const char* nextValue = NULL;

	/* 剔除字符串为空的情况 */
	if (jsonStr == NULL || model == NULL || modelCount == 0)
	{
		JSONErrorCode = PARAM_IS_EMPTY;//字符串为空 失败
		return NULL;
	}

	/*
	* 检查是不是空对象
	* 子对象的末尾 } 已经被替换为 '\0'
	*/
	if (*(value + 1) == '\0')
	{
		JSONErrorCode = DECODE_OBJECT_IS_EMPTY;//对象为空
		return NULL;
	}
	value++;//跳过字符 {
	nextValue = value;

	/* 获取用户定义的结构体大小并分配空间 */
	for (unsigned i = 0; i < modelCount; i++)
	{
		if (model[i].type == JSON_TYPE_OBJECT)
		{
			objectSize = model[i].value.objectSize;
			break;
		}
	}

	obj = JSON_malloc(objectSize);//申请空间
	if (obj == NULL)
	{
		JSONErrorCode = DECODE_OBJECT_FAIL_1;//为对象申请空间失败
		return NULL;
	}
	memset(obj, 0, objectSize);

#if (JSON_DEBUG == 1)
	printf("对象申请空间%p\n", obj);
#endif // (JSON_DEBUG == 1)

	/*
	* 解析数据
	* 跳过根节点
	*/
	for (unsigned int i = 1; i < modelCount; i++)
	{
		/*
		* 判断下一个数据模型是不是子对象/数组类型
		* 如果是则将父对象的JSON字符串的的当前位置移动到子对象/数组的JSON字符串的末尾
		* 并在子对象/数组JSON字符串末尾添加字符串结束标志
		*/
		if ((model[i].type == JSON_TYPE_OBJECT || model[i].type == JSON_TYPE_ARRAY))
		{
			value = JSONSkipChildJsonStr((char**)&nextValue, model[i].type);
			if (value == NULL)
			{
				JSONErrorCode = NOU_FIND_CHILD_ARRAY_OBJECT_END;//未找到子对象/数组的JSON字符串末尾
			}
		}
		else
		{
			/* 获取下一个需要解析的JSON字符串 */
			value = strtok_s(NULL, ",", (char**)&nextValue);
		}

		switch (model[i].type)
		{
		case JSON_TYPE_BOOL:
			*(JSON_BOOL_t*)((char*)obj + model[i].offset) = DecodeBool(value, &model[i], 1);
			break;
		case JSON_TYPE_CHAR:
			*(char*)((char*)obj + model[i].offset) = (char)DecodeNumberOrDouble(value, &model[i], 1);
			break;
		case JSON_TYPE_SHORT:
			*(short*)((char*)obj + model[i].offset) = (short)DecodeNumberOrDouble(value, &model[i], 1);
			break;
		case JSON_TYPE_INT:
			*(int*)((char*)obj + model[i].offset) = (int)DecodeNumberOrDouble(value, &model[i], 1);
			break;
		case JSON_TYPE_UCHAR:
			*(unsigned char*)((char*)obj + model[i].offset) = (unsigned char)DecodeNumberOrDouble(value, &model[i], 1);
			break;
		case JSON_TYPE_USHORT:
			*(unsigned short*)((char*)obj + model[i].offset) = (unsigned short)DecodeNumberOrDouble(value, &model[i], 1);
			break;
		case JSON_TYPE_UINT:
			*(unsigned int*)((char*)obj + model[i].offset) = (unsigned int)DecodeNumberOrDouble(value, &model[i], 1);
			break;
		case JSON_TYPE_FLOAT:
			*(float*)((char*)obj + model[i].offset) = (float)DecodeNumberOrDouble(value, &model[i], 1);
			break;
		case JSON_TYPE_DOUBLE:
			*(double*)((char*)obj + model[i].offset) = (double)DecodeNumberOrDouble(value, &model[i], 1);
			break;
		case JSON_TYPE_STRING:
			*(char**)((char*)obj + model[i].offset) = DecodeString(value, &model[i], 1);
			break;
		case JSON_TYPE_OBJECT:
			/* 子对象 */
			/* 检查子对象的键名与JSON字符串中的是否一致 */
			value = CheckKeyName(value, &model[i]);
			if (value == NULL)
			{
				JSONErrorCode = DECODE_OBJECT_FAIL;//键名不一致解析失败
				//释放对象申请的空间
				FreeJSONObject(obj, model, modelCount);
				return NULL;
			}

			/* 解析子对象 */
			void* childObj = NULL;
			childObj = DecodeObject(value, model[i].value.child.model, model[i].value.child.count);
			if (childObj == NULL)//解析子对象失败
			{
				if (JSONErrorCode == DECODE_OBJECT_IS_EMPTY)//是不是空对象
				{
					JSONErrorCode = JSON_SUCCESS;//复位错误代码
				}
				else
				{
					JSONErrorCode = DECODE_OBJECT_FAIL_2;//解析子对象失败
					return NULL;
				}
			}
			*(char**)((char*)obj + model[i].offset) = childObj;
			break;
		case JSON_TYPE_ARRAY:
			DecodeArray(value, &model[i], (char*)obj + model[i].offset, 1);
			if (JSONErrorCode == DECODE_ARRAY_IS_EMPTY)//是不是空数组
			{
				JSONErrorCode = JSON_SUCCESS;//复位错误代码
			}
			break;
		default:
			break;
		}

		/* 检查错误代码 */
		if (JSONErrorCode != JSON_SUCCESS)
		{
			//释放对象申请的空间
			FreeJSONObject(obj, model, modelCount);
			return NULL;
		}
	}

	return obj;
}

解析实数

  1. 检查字符串是否可读
  2. 转换实数
/** \brief  解码JSON字符串中的整数/实数
 *
 * \param   jsonStr 整数/实数的JSON字符串
 * \param   model 整数/实数的数据模型(如果 decodeKeyName 为 0 则 model 可为 NULL)
 * \param   decodeKeyName 是否解码键名
 *					decodeKeyName = 1 解码键名(JSON字符串能包含整数的键名于字符 : )
 *					decodeKeyName = 0 不解码键名(JSON字符串能包含整数的键名于字符 : )
 * \return  解码成功则返回解析得到的整数/实数的值
 *				解码失败则返回 NULL 可通过 JSONErrorCode() 查看错误代码
 *
 */
static double DecodeNumberOrDouble(const char* jsonStr, PJSON_MODEL_t model, char decodeKeyName)
{
	double d = 0.0;
	const char* value = jsonStr;
	const char* endStr = NULL;

	if (value == NULL)//检查字符串是否可读
	{
		return 0.0;
	}

	if (decodeKeyName)
	{
		value = CheckKeyName(jsonStr, model);
		if (value == NULL)
		{
			JSONErrorCode = DECODE_NUMBER_DOUBLE_FAIL; //解析整数/实数失败
			return 0.0;
		}
	}

	d = strtod(value, (char**)&endStr);
	if (*endStr != 'n' && *endStr != '\0' && *endStr != '}')//如果不是 null  \0 } 则解析失败
	{
		JSONErrorCode = DECODE_NUMBER_DOUBLE_FAIL; //解析整数/实数失败
	}

	return d;
}

释放对象

  1. 遍历对象的数据模型
  2. 如果是实数类型,则继续
  3. 字符串类型则释放空间
  4. 对象类型则递归调用此函数
  5. 数组类型则根据数组元素类型来释放空间
/** \brief  释放解码过的对象
 *
 * \param   obj 对象的地址
 * \param   model 对象的数据模型
 * \param   modelCount 对象的数据模型数量
 * \return  无
 *
 */
void FreeJSONObject(void* obj, PJSON_MODEL_t model, unsigned int modelCount)
{
	void* addr = NULL;

	/* 剔除对象为空的情况 */
	if (obj == NULL || model == NULL || modelCount == 0)
	{
		JSONErrorCode = PARAM_IS_EMPTY;//参数为空失败
		return;
	}

	if (JSON_malloc == NULL || JSON_free == NULL)
	{
		JSONErrorCode = JSON_NOT_INIT;//未初始化
		return;
	}

	for (unsigned int i = 0; i < modelCount; i++)
	{
		switch (model[i].type)
		{
		case JSON_TYPE_BOOL:
		case JSON_TYPE_CHAR:
		case JSON_TYPE_SHORT:
		case JSON_TYPE_INT:
		case JSON_TYPE_UCHAR:
		case JSON_TYPE_USHORT:
		case JSON_TYPE_UINT:
		case JSON_TYPE_FLOAT:
		case JSON_TYPE_DOUBLE:
			break;
		case JSON_TYPE_STRING:
			addr = *(char**)((char*)obj + model[i].offset);//字符串的地址
			if (addr)
			{
#if (JSON_DEBUG == 1)
				printf("释放字符串%p  %s\n", addr, addr);
#endif // (JSON_DEBUG == 1)

				JSON_free(addr);
			}
			break;
		case JSON_TYPE_OBJECT:
			if (model[i].keyName == NULL)
			{
				continue;
			}
			addr = *(char**)((char*)obj + model[i].offset);//对象的地址
			if (addr)
			{
				FreeJSONObject(addr, model[i].value.child.model, model[i].value.child.count);
			}
			break;
		case JSON_TYPE_ARRAY:
			switch (model[i].value.array.type)
			{
			case JSON_TYPE_BOOL:
			case JSON_TYPE_CHAR:
			case JSON_TYPE_SHORT:
			case JSON_TYPE_INT:
			case JSON_TYPE_UCHAR:
			case JSON_TYPE_USHORT:
			case JSON_TYPE_UINT:
			case JSON_TYPE_FLOAT:
			case JSON_TYPE_DOUBLE:
				break;
			case JSON_TYPE_STRING:
				for (unsigned int j = 0; j < model[i].value.array.count; j++)
				{
					addr = *(char**)((char*)obj + model[i].offset + j * model[i].value.array.size);
					if (addr)
					{
#if (JSON_DEBUG == 1)
						printf("释放字符串%p  %s\n", addr, addr);
#endif // (JSON_DEBUG == 1)

						JSON_free(addr);
					}
				}
				break;
			case JSON_TYPE_OBJECT:
				for (unsigned int j = 0; j < model[i].value.array.count; j++)
				{
					addr = *(char**)((char*)obj + model[i].offset + j * model[i].value.array.size);
					if (addr)
					{
						FreeJSONObject(addr, model[i].value.array.child.model, model[i].value.array.child.count);
					}
				}
				break;
			default:
				break;
			}
			break;
		default:
			break;
		}
	}

#if (JSON_DEBUG == 1)
	printf("释放对象%p\n", obj);
#endif // (JSON_DEBUG == 1)
	JSON_free(obj);
}

编码

JSON编码

  1. 先计算根对象需要的内存空间大小
  2. 调用编码对象功能函数编码JSON字符串
/** \brief  编码JSON字符串
 *
 * \param   obj 对象
 * \param   model 数据模型
 * \param   modelCount 数据模型数量
 * \return  成功则返回已编码的JSON字符串
 *				失败则返回 NULL 可通过JSONErrorCode查看错误代码
 *
 * \note	使用完记得调用 FreeJSONString() 来释放字符串申请的空间
 */
char* EncodeJSON(void* obj, PJSON_MODEL_t model, unsigned int modelCount)
{
	char* encodeJSONStr = NULL;
	char* str = NULL;
	BYTES_t bytes = 0;

	/* 复位错误标志 */
	JSONErrorCode = JSON_SUCCESS;

	if (obj == NULL || model == NULL || modelCount == 0)
	{
		JSONErrorCode = PARAM_IS_EMPTY;//函数参数无效
		return NULL;
	}

	if (JSON_malloc == NULL || JSON_free == NULL)
	{
		JSONErrorCode = JSON_NOT_INIT;//未初始化
		return;
	}

	/* 计算对象需要的字节数量 */
	bytes = GetObjectNeedBytes(obj, model, modelCount);
	if (bytes == 0)
	{
		return NULL;
	}

#if (JSON_DEBUG == 1)
	printf("编码的JSON字符串长度为%u\n", bytes);
#endif // (JSON_DEBUG == 1)

	/* 为JSON字符串申请内存空间 */
	bytes++;
	encodeJSONStr = JSON_malloc(bytes);
	if (encodeJSONStr == NULL)
	{
		JSONErrorCode = ENCODE_FAIL;//申请空间失败
		return NULL;
	}
	memset(encodeJSONStr, 0, bytes);

#if (JSON_DEBUG == 1)
	printf("编码的JSON字符串长度申请空间%p\n", encodeJSONStr);
#endif // (JSON_DEBUG == 1)

	/* 编码JSON字符串 */
	str = encodeJSONStr;
	str = EncodeObject(obj, model, modelCount, str);
	*str = '\0';//添加字符串结束标志

#if (JSON_DEBUG == 1)
	printf("编码的JSON字符串为%s\n", encodeJSONStr);
#endif // (JSON_DEBUG == 1)

	return encodeJSONStr;
}

demo

JSON字符串如下

{
  "id": "0011223344",
  "name": "张三",
  "phone": "111-22223333",
  "addr": "天安门广场",
  "grade": "小学四年级",
  "score": {
	"math": 99.5,
	"chinese": 85.5,
	"english": 86,
	"science": 86.5,
	"history": 92
  }
}

对应的结构体定义如下

/* 分数对象 */
typedef struct score
{
	float math;
	float chinese;
	float english;
	float science;
	float history;
}score_t, * Pscore_t;

/* 学生对象 */
typedef struct stu
{
	char* id;
	char* name;
	char* phone;
	char* addr;
	char* grade;
	Pscore_t score;
}stu_t, * Pstu_t;

相应的数据模型

/* 分数对象数据模型 */ 
JSON_MODEL_t scoreModel[] =
{
	JSON_ROOT_OBJECT(score_t),
	JSON_FLOAT(score_t, math),
	JSON_FLOAT(score_t, chinese),
	JSON_FLOAT(score_t, english),
	JSON_FLOAT(score_t, science),
	JSON_FLOAT(score_t, history)
};

/* 学生对象数据模型 */
JSON_MODEL_t stuModel[] =
{
	JSON_ROOT_OBJECT(stu_t),
	JSON_STRING(stu_t, id),
	JSON_STRING(stu_t, name),
	JSON_STRING(stu_t, phone),
	JSON_STRING(stu_t, addr),
	JSON_STRING(stu_t, grade),
	JSON_OBJECT(stu_t, score, scoreModel, sizeof(scoreModel) / sizeof(JSON_MODEL_t))
};

解码/编码

	JSONInit(malloc, free);

	char jsonStr[] = "\
	{\
		\"id\": \"0011223344\",\
			\"name\" : \"张三\",\
			\"phone\" : \"111-22223333\",\
			\"addr\" : \"天安门广场\",\
			\"grade\" : \"小学四年级\",\
			\"score\" : {\
			\"math\": 99.5,\
				\"chinese\" : 85.5,\
				\"english\" : 86,\
				\"science\" : 86.5,\
				\"history\" : 92\
		}\
	}\
		";
	/* 解码 */
	Pstu_t stu = DecodeJSON(jsonStr, stuModel, sizeof(stuModel) / sizeof(JSON_MODEL_t), 1);

	if (stu == NULL)
	{
		printf("stu解码失败 errorCode = %d\n", JSONErrorCode);
		return;
	}

	if (stu->id == NULL)
	{
		printf("学号为空\n");
	}
	else
	{
		printf("学号为%s\n", stu->id);
	}

	if (stu->name == NULL)
	{
		printf("姓名为空\n");
	}
	else
	{
		printf("姓名为%s\n", stu->name);
	}

	if (stu->phone == NULL)
	{
		printf("手机号为空\n");
	}
	else
	{
		printf("手机号为%s\n", stu->phone);
	}

	if (stu->addr == NULL)
	{
		printf("住址为空\n");
	}
	else
	{
		printf("住址为%s\n", stu->addr);
	}

	if (stu->grade == NULL)
	{
		printf("年级为空\n");
	}
	else
	{
		printf("年级为%s\n", stu->grade);
	}

	if (stu->score == NULL)
	{
		printf("成绩为空\n");
	}
	else
	{
		printf("数学:%.2f  语文:%.2f  英语:%.2f  科学:%.2f  历史:%.2f\n",
			stu->score->math, stu->score->chinese, stu->score->english, stu->score->science, stu->score->history);
	}
	printf("\n\n");

	/* 编码 */
	char* str = EncodeJSON(stu, stuModel, sizeof(stuModel) / sizeof(JSON_MODEL_t));

	if (str == NULL)
	{
		printf("编码失败\n");
		return;
	}

	printf("%s\n", str);


	/* 释放空间 */
	FreeJSONString(str);
	FreeJSONObject(stu, stuModel, sizeof(stuModel) / sizeof(JSON_MODEL_t));

输出结果
在这里插入图片描述

C语言实现JSON字符串解码与编码(一)大致思路

C语言实现JSON字符串解码与编码(二)头文件介绍

C语言实现JSON字符串解码与编码(三)源代码

源代码下载链接

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-28 11:37:29  更:2022-04-28 11:37:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 21:17:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码