申请/释放空间
因为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字符串,与对应的数据模型,即可解码。
- 检查参数
- 检查申请/释放空间函数指针是否已经赋值
- 根对象是否为空
- 调用解析对象功能函数开始解析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;
}
解析对象
- 检查函数参数
- 检查对象为空
- 获取根对象占用的字节大小
- 为对象申请空间
- 变量对象的数据模型,依次解析JSON字符串
- 检查错误代码,出错则释放对象,函数返回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;
}
解析实数
- 检查字符串是否可读
- 转换实数
/** \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;
}
释放对象
- 遍历对象的数据模型
- 如果是实数类型,则继续
- 字符串类型则释放空间
- 对象类型则递归调用此函数
- 数组类型则根据数组元素类型来释放空间
/** \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编码
- 先计算根对象需要的内存空间大小
- 调用编码对象功能函数编码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));
输出结果
源代码下载链接