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

JSON字符串的解码

假如有这么一个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字符串函数的通用性,定义一个变量来存储对象占用的字节大小。
总的来说,解码有以下几点:

  1. 对象占用字节大小,因为,解码对象时候,需要为对象申请一个空间。
  2. 成员的类型,不同的类型的解码过程不一样
  3. 成员变量的地址(相对于结构体变量的偏移量)
  4. 键名,通过比对键名与JSON字符串中的键名,来判断数据是否正确,对号入座。
  5. 子对象及数组相关信息。

存储结构体的成员相对于结构体的偏移量

在解析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": "张三",//姓名

idname就是键名

存储子对象相关的信息

子对象成员类型,子对象成员个数

存储数组相关的信息

数组元素类型,元素个数,元素大小

JSON数据模型

最终可以得到这么一个结构体

/* 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字符串, 有以下几点:

  1. 生成JSON字符串的长度,也就是根据对象来得到JSON字符串需要占用的字节大小。
  2. 得到JSON字符串的地址,访问对象的成员,根据成员类型调用相应的功能函数向字符串写入数据。

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

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

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

源代码下载链接

你可能感兴趣的:(JSON,c语言,json)