我是靠谱客的博主 细心黑猫,最近开发中收集的这篇文章主要介绍cJSON源码解析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述


   cJSONC语言中的一个JSON编解码器,非常轻量级,C开源代码汇总,代码量少,不到一千行,代码的可读性高,很适合作为 C 语言项目进行学习。
   对于 json 格式编码与解码,其实就是类似于一个解释器,主要的原理是运用递归。
  首先看下 cJSON.h 中定义的 cJSON数据结构,

typedef struct cJSON {
	struct cJSON *next,*prev;	/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
	struct cJSON *child;		/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */

	int type;					/* The type of the item, as above. */

	char *valuestring;			/* The item's string, if type==cJSON_String */
	int valueint;				/* The item's number, if type==cJSON_Number */
	double valuedouble;			/* The item's number, if type==cJSON_Number */

	char *string;				/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;

  不管是数据类型、字符串类型还是对象类型都使用这个结构体,类型的信息通过type 来标记, cJSON 共定义了 7 中类型。

/* 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  //array 类型
#define cJSON_Object 6  //对象类型

   如果是对象或者数组,采用的是双向链表来实现,链表中的每个节点表示数组中的一个元素或者对象中的一个字段。其中 child 表示头结点,nextprev 分别表示下一个节点和前一个节点。valuestringvalueintvaluedouble分别表示字符串、整数、浮点数的字面量。string 表示对象中的某一字段的名称,比如有这样的一个json 字符串,其中的 age 就用 string来表示。

{'age' : 20}

   cJSON 的 API 使用起来很简单,text 表示你需要解析的文件内容,注意释放内存,以免泄露。

	char *out;cJSON *json;
	
	json=cJSON_Parse(text);
	if (!json) {printf("Error before: [%s]n",cJSON_GetErrorPtr());}
	else
	{
		out=cJSON_Print(json);
		cJSON_Delete(json);
		printf("%sn",out);
		free(out);
	}

1、JSON的解析

   进行解析分析前,我们先了解下 JSON 文件的格式。

Some JSON:
{
    "name": "Jack ("Bee") Nimble", 
    "format": {
        "type":       "rect", 
        "width":      1920, 
        "height":     1080, 
        "interlace":  false, 
        "frame rate": 24
    }
}

   JSON 的字符串的解析主要是通过 cJSON_Parse 函数来完成的。

cJSON *cJSON_Parse(const char *value) 
	{return cJSON_ParseWithOpts(value,0,0);}
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
	const char *end=0;
	cJSON *c=cJSON_New_Item();
	ep=0;
	if (!c) return 0;       /* memory fail */

	end=parse_value(c,skip(value));
	if (!end)	{cJSON_Delete(c);return 0;}	/* parse failure. ep is set. */

	/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
	if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
	if (return_parse_end) *return_parse_end=end;
	return c;
}
  • 第一步:先调用 cJSON_New_Item函数 创建一个 cJSON 节点,函数就是为 节点分配内存,并将对应的内存初始化为 0;
static cJSON *cJSON_New_Item(void)
{
	cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
	if (node) memset(node,0,sizeof(cJSON));
	return node;
}
  • 调用 parse_value 函数解析文件,该函数是核心部分,在解析前,先skip(value) ,这里的意思就是将字符串前面的空格去掉;
static const char *skip(const char *in){	
	while (in && *in && (unsigned char)*in<=32) 
		in++; 
		return in;
	}

  • 最后一步中,函数的参数中提供的参数 require_null_terminated 是为了确认 JSON 字符串必须以'' 字符结尾。若参数提供了 require_null_terminated ,表示将 返回 JSON 字符串解析完后剩余的部分。

2、 parse_value 函数

static const char *parse_value(cJSON *item,const char *value)
{
	if (!value)						
		return 0;	/* Fail on null. */
	//如果value为 `null` ,直接将type 标记为 `cJSON_NULL`
	if (!strncmp(value,"null",4))	{ 
		item->type=cJSON_NULL;  
		return value+4; 
	}
	//如果value的值为 false ,则将type 标记为 cJSON_False
	if (!strncmp(value,"false",5))	{ 
		item->type=cJSON_False; 
		return value+5; 
	}
	if (!strncmp(value,"true",4))	{ 
		item->type=cJSON_True; 
		item->valueint=1;	
		return value+4; 
	}
	//如果value以 '"' 开头,表示这是一个字符串
	if (*value=='"')				{
		 return parse_string(item,value); 
	}
	//如果 value 以 '0-9' 开头,表示是数字
	if (*value=='-' || (*value>='0' && *value<='9'))	{
		 return parse_number(item,value); 
	}
	//如果 value  以 '[' 开头, 表示是array
	if (*value=='[')				{ 
		return parse_array(item,value); 
	}
	//如果 value 以 '{' 开头 , 表示是对象
	if (*value=='{')				{ 
		return parse_object(item,value); 
	}
	ep=value;
	return 0;	/* failure. */
}
  • 函数 parse_string,这个函数中需要注意的是遇到转移字符的处理;
tatic const char *parse_string(cJSON *item,const char *str)
{
	const char *ptr=str+1;
	char *ptr2;
	char *out;
	int len=0;
	unsigned uc,uc2;
	if (*str!='"') { //重复确保是一个字符串
		ep=str;
		return 0;
	}	/* not a string! */
	
	while (*ptr!='"' && *ptr && ++len)  //跳过注释行
		if (*ptr++ == '\') 
			ptr++;	/* Skip escaped quotes. */
	//分配内存,以保存解析之后的结果
	out=(char*)cJSON_malloc(len+1);	/* This is how long we need for the string, roughly. */
	if (!out) return 0;
	
	ptr=str+1;
	ptr2=out;
	while (*ptr!='"' && *ptr)
	//如果遇到'' 表示达到字符串的末尾,解析结束
	{
		if (*ptr!='\') 
			*ptr2++=*ptr++;//如果不是转义字符,直接复制内容
		else
		{
			ptr++;
			switch (*ptr)
			{
				case 'b': *ptr2++='b';	break;
				case 'f': *ptr2++='f';	break;
				case 'n': *ptr2++='n';	break;
				case 'r': *ptr2++='r';	break;
				case 't': *ptr2++='t';	break;
				case 'u':	 /* transcode utf16 to utf8. */
					uc=parse_hex4(ptr+1);
					ptr+=4;	/* get the unicode char. */

					if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0)
						break;	/* check for invalid.	*/

					if (uc>=0xD800 && uc<=0xDBFF)	/* UTF16 surrogate pairs.	*/
					{
						if (ptr[1]!='\' || ptr[2]!='u')	
							break;	/* missing second-half of surrogate.	*/
						uc2=parse_hex4(ptr+3);
						ptr+=6;
						if (uc2<0xDC00 || uc2>0xDFFF)		
							break;	/* invalid second-half of surrogate.	*/
						uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
					}

					len=4;
					if (uc<0x80) 
						len=1;
					else if (uc<0x800) 
						len=2;
					else if (uc<0x10000) 
						len=3; 
						ptr2+=len;					
					switch (len) {
						case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
						case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
						case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
						case 1: *--ptr2 =(uc | firstByteMark[len]);
					}
					ptr2+=len;
					break;
				default:  
					*ptr2++=*ptr; 
					break;
			}
			ptr++;
		}
	}
	*ptr2=0;
	if (*ptr=='"') ptr++;
	item->valuestring=out;
	item->type=cJSON_String;
	return ptr;
}
  • 函数 parse_number,解析数字很简单,注意符号、小数、指数就差不多了;
tatic const char *parse_number(cJSON *item,const char *num)
{
	double n=0,
	sign=1,scale=0;
	int subscale=0,signsubscale=1;

	if (*num=='-') sign=-1,num++;	/* Has sign? */
	if (*num=='0') //直接跳过数字前面的无效的 0 
		num++;			/* is zero */
	if (*num>='1' && *num<='9')	
		do	
			n=(n*10.0)+(*num++ -'0');	
		while (*num>='0' && *num<='9');	/* Number? */
	if (*num=='.' && num[1]>='0' && num[1]<='9') {
		num++;		
		do	
			n=(n*10.0)+(*num++ -'0'),scale--; 
		while (*num>='0' && *num<='9');}	/* Fractional part? */
	if (*num=='e' || *num=='E')		/* Exponent? */
	{	num++;
		if (*num=='+') 
			num++;	
		else if (*num=='-') signsubscale=-1,num++;		/* With sign? */
		while (*num>='0' && *num<='9') 
			subscale=(subscale*10)+(*num++ - '0');	/* Number? */
	}

	n=sign*n*pow(10.0,(scale+subscale*signsubscale));	/* number = +/- number.fraction * 10^+/- exponent */
	
	item->valuedouble=n;
	item->valueint=(int)n;
	item->type=cJSON_Number;
	return num;
}
  • 函数 parse_array,解析数组的基本思想是对数组中的每一个元素递归调用 parse_value,再将元素连接形成一个链表;
static const char *parse_array(cJSON *item,const char *value)
{
	cJSON *child;
	if (*value!='[')	{
		ep=value;
		return 0;}	/* not an array! */

	item->type=cJSON_Array;
	value=skip(value+1);//跳过空白
	if (*value==']') 
		return value+1;	/* empty array. */

	item->child=child=cJSON_New_Item();//每一个数组元素对应一个节点
	if (!item->child)
		 return 0;		 /* memory fail */
	value=skip(parse_value(child,skip(value)));	/* skip any spacing, get the value. */
	if (!value) 
		return 0;
//如果元素后面还有',' 表示数组中还有元素,对剩下的元素继续进行上面的操作,构成链表
	while (*value==',')
	{
		cJSON *new_item;
		if (!(new_item=cJSON_New_Item())) 
			return 0; 	/* memory fail */
		child->next=new_item;
		new_item->prev=child;
		child=new_item;
		value=skip(parse_value(child,skip(value+1)));
		if (!value) 
			return 0;	/* memory fail */
	}

	if (*value==']') 
		return value+1;	/* end of array */
	ep=value;
	return 0;	/* malformed. */
}
  • 函数 parse_object
static const char *parse_object(cJSON *item,const char *value)
{
	cJSON *child;
	if (*value!='{')	{ep=value;return 0;}	/* not an object! */
	
	item->type=cJSON_Object;
	value=skip(value+1);
	if (*value=='}') return value+1;	/* empty array. */
	
	item->child=child=cJSON_New_Item();
	if (!item->child) return 0;
	value=skip(parse_string(child,skip(value)));
	if (!value) return 0;
	child->string=child->valuestring;child->valuestring=0;
	if (*value!=':') {ep=value;return 0;}	/* fail! */
	value=skip(parse_value(child,skip(value+1)));	/* skip any spacing, get the value. */
	if (!value) return 0;
	
	while (*value==',')
	{
		cJSON *new_item;
		if (!(new_item=cJSON_New_Item()))	return 0; /* memory fail */
		child->next=new_item;new_item->prev=child;child=new_item;
		value=skip(parse_string(child,skip(value+1)));
		if (!value) return 0;
		child->string=child->valuestring;child->valuestring=0;
		if (*value!=':') {ep=value;return 0;}	/* fail! */
		value=skip(parse_value(child,skip(value+1)));	/* skip any spacing, get the value. */
		if (!value) return 0;
	}
	
	if (*value=='}') return value+1;	/* end of array */
	ep=value;return 0;	/* malformed. */
}

  客户端代码解析完之后需要释放内存,cJSON_Delete

void cJSON_Delete(cJSON *c)
{
	cJSON *next;
	while (c)
	{
		next=c->next;
		if (!(c->type&cJSON_IsReference) && c->child) 
			cJSON_Delete(c->child);
		if (!(c->type&cJSON_IsReference) && c->valuestring)
			 cJSON_free(c->valuestring);
		if (!(c->type&cJSON_StringIsConst) && c->string)
			 cJSON_free(c->string);
		cJSON_free(c);
		c=next;
	}
}

最后

以上就是细心黑猫为你收集整理的cJSON源码解析的全部内容,希望文章能够帮你解决cJSON源码解析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部