假如有这么一个JSON字符串,其有字符串,实数,对象类型。
学生信息的JSON字符串
{
"id": "0011223344",//学号
"name": "张三",//姓名
"phone": "111-22223333",//手机号
"addr": "天安门广场",//住址
"grade": "小学四年级",//年级
"score": {//分数对象
"math": 99.5,//数学得分
"chinese": 85.5,
"english": 86,
"science": 86.5,
"history": 92
}
}
在C语言中,结构体可以存储多种类型的变量
故定义一个结构体来存储该学生对象。
学生类型的结构体(对象)定义
typedef struct stu
{
char* id;//学号
char* name;//姓名
char* phone;//手机号
char* addr;//住址
char* grade;//年级
Pscore_t score;//分数
}stu_t, * Pstu_t;
其中Pstu_t
是指向struct stu
的指针类型。
在解码JSON字符串的时候,按照从第一个字符到最后一个字符的顺序。为了解码,我们需要对象(结构体)的大小,对象的成员类型,存储对象成员的变量(变量的地址),用于区分每个成员的键值对的键名(我不知道这个名词叫啥,暂且叫它键名吧),如果含有数组成员,则需要数组的元素类型,元素个数,元素大小,如果含有子对象,则需要子对象的类型,占用的字节大小,对于指针类型(对象/字符串)还得知道占用的字节大小,字符串可以使用strlen()
求出,对象则可以使用sizeof(stu_t)
求出,考虑到解码JSON字符串函数的通用性,定义一个变量来存储对象占用的字节大小。
总的来说,解码有以下几点:
在解析JSON字符串的时候,需要把相应的信息,放入对应的变量中。
比如将学号信息"id": "0011223344",//学号
放入到学生结构体id
变量中,由于类型为字符串,为此还需要在堆中开辟一块内存存储该字符串。如果是普通数据类型则不需要了。
为此,需要存储该变量的在结构体中偏移值。可以通过宏offsetof
来计算结构体成员相对于结构体的偏移值。在解码的时候,只需要传入结构体变量的地址即可。
比如stu_t
的成员id
相对于结构体的偏移值为0
,宏offsetof(stu_t, id)
的值为0
成员name
的偏移值为8
,宏offsetof(stu_t, name)
的值为8
#define offsetof(s,m) ((size_t)&(((s*)0)->m))//宏offsetof展开
还需要存储该数据的类型,如果是字符串类型,则将指向字符串的指针赋值为字符串的地址,并在堆开辟一块内存来存储该字符串,如果是普通数据类型类型,则调用strtod()/strtol()/atol()/atof()
等函数
将字符串转换位实数。如果是对象,则将指向对象的指针赋值为对象的地址(也就是结构体的指针),如果是数组类型,则需要数组元素的类型,解析字符串,如果是布尔类型,则解析是true/false/null
JSON数据类型枚举:
/* JSON的数据类型 */
typedef enum
{
JSON_TYPE_BOOL = 0,//布尔类型的枚举 JSON_BOOL_t false = -1, true = 1, null = 2
JSON_TYPE_CHAR,
JSON_TYPE_SHORT,
JSON_TYPE_INT,
JSON_TYPE_UCHAR,
JSON_TYPE_USHORT,
JSON_TYPE_UINT,
JSON_TYPE_FLOAT,
JSON_TYPE_DOUBLE,
JSON_TYPE_STRING,
JSON_TYPE_OBJECT,
JSON_TYPE_ARRAY
}JSON_Type_t;
对象与字符串一样,最终得到是一个地址,对象(结构体的地址),也是需要在堆中开辟一块空间,来存储对象,所以需要一个变量来存储结构体的大小sizeof(stu_t)
如
"id": "0011223344",//学号
"name": "张三",//姓名
id
与name
就是键名
子对象成员类型,子对象成员个数
数组元素类型,元素个数,元素大小
最终可以得到这么一个结构体
/* JSON数据模型结构体 */
typedef struct JSON_MODEL
{
JSON_Type_t type;//数据类型
const char* keyName;//键名
unsigned short offset;//偏移
union
{
unsigned short objectSize;//对象大小 单位字节
/* 子对象 */
struct ChildObject
{
struct JSON_MODEL* model;//子对象的数据模型的地址
unsigned int count;//子对象数据模型个数
}child;
/* 数组 */
struct
{
JSON_Type_t type;//数组元素类型
unsigned int count;//数组元素个数
unsigned short size;//数组元素大小 单位字节
struct ChildObject child;//如果数组元素是对象则需要填写这个
}array;
}value;
}JSON_MODEL_t, * PJSON_MODEL_t;
定义一个JSON_MODEL_t
的数组rootModel,往这个数组中添加元素,每一个元素代表一个成员。
如学生对象
JSON_MODEL_t rootModel[] =
{
{OBJECT/*对象类型*/, NULL/*根对象无键名*/, 0/*偏移值*/, .value.objectSize = sizeof(stu_t)/*根对象的大小*/},//学生结构体对象
/* 成员类型 */
{STRING/*字符串类型*/, "id"/*键名*/,offsetof(stu_t, id)/*偏移值*/},//学号
{STRING/*字符串类型*/, "name"/*键名*/,offsetof(stu_t, name)/*偏移值*/},//姓名
/* 省略 住址/手机号/年级 */
{OBJECT/*对象类型*/, "socre"/*分数对象键名*/, offsetof(stu_t, score)/*偏移值*/, .value.child.model = childModel/*子对象数据模型*/, .value.child.count = childModelCount/*子对象数据模型个数*/}//分数对象
}
相对于解码,编码就简单些。
根据对象来生成JSON字符串, 有以下几点:
C语言实现JSON字符串解码与编码(一)大致思路
C语言实现JSON字符串解码与编码(二)头文件介绍
C语言实现JSON字符串解码与编码(三)源代码
源代码下载链接