json库源码阅读

JSON.h**

#ifndef cJSON__h
#define cJSON__h

#ifdef __cplusplus
//extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。这样的话cjson库在c++中也可以使用了,也就是c和c++混合编译。
extern "C"
{
#endif

//以宏的方式定义出的几种cJson对象的类型
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
	
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512

//cJSON的数据结构,是cjson库很重要的数据结构。
typedef struct cJSON 
{
	/* next/prev 用来遍历所有的数组或者对象链表. 一般来说可以调用	                                   		  GetArraySize/GetArrayItem/GetObjectItem 进行操作*/
 
    //同级的键值对,对象之间用前后指针来互相访问,被包含的对象,用孩子指针指向。
	struct cJSON *next,*prev;	
	/* 一个数组或者对象会有一个孩子节点指针指向一个对象或者数组链 */
	struct cJSON *child;		
    
	/* 这个节点的类型, 为上面定义的宏 */
	int type;					
	/* 是节点的值 如果节点的类型是cJSON_String的话 */
	char *valuestring;			
	/* 是节点的值 如果节点的类型是cJSON_Number的话 ,这个是整数类型*/
	int valueint;				
	/* 是节点的值 如果节点的类型是cJSON_Number的话,这个是浮点数类型 */
	double valuedouble;
    
	/* 节点的名字,即键值对中键的名字或对象的名字*/
	char *string;				
} cJSON;
    
//钩子结构体,将申请内存和释放内存的接口打包,这样释放时候两个都能考虑到,方便管理和初始化。
typedef struct cJSON_Hooks {
      void *(*malloc_fn)(size_t sz);
      void (*free_fn)(void *ptr);
} cJSON_Hooks;


/*-------------------------------init and printf-----------------------------------------*/

//初始化使用json的时候内存的分配和释放方式,一般是malloc和free。
extern void cJSON_InitHooks(cJSON_Hooks* hooks);

//用默认的方式对字符串进行解析,存储在cjson格式的结构体中。是cjson的核心
extern cJSON *cJSON_Parse(const char *value);

//打印文本json
extern char  *cJSON_Print(cJSON *item);

extern char  *cJSON_PrintUnformatted(cJSON *item);

extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);

//删除cjson节点,(同时也会释放他的所有的孩子节点,相当于删除整颗链表和树)
extern void   cJSON_Delete(cJSON *c);

extern int	  cJSON_GetArraySize(cJSON *array);

extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);

extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);

extern const char *cJSON_GetErrorPtr(void);
     
/*---------------------------------------create---------------------------------------*/     

//用来建立指定类型的cjson节点
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);

//用来建立指定类型的数组节点
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);

/*---------------------------------------add---------------------------------------*/       

//将指定的cJSON结构item加入array或object中
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void	cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
extern void	cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item);	
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);

/*---------------------------------------delect---------------------------------------*/ 

//从一个数组,对象之中删除指定的节点
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void   cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void   cJSON_DeleteItemFromObject(cJSON *object,const char *string);

/*------------------------------------updata and copy------------------------------------*/ 

extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem);	
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
   
//复制一个cjson节点,是否复制孩子节点取决于recurse的值,!recurse = 0时会复制孩子节点(即=0时为不复制子节点,=其他值的时候不复制子节点),复制后的节点在内存中需要释放。返回复制的节点的指针。
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);

//解析,extern cJSON *cJSON_Parse(const char *value);就是利用它实现的。
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);

//一个精简后的解析框架
extern void cJSON_Minify(char *json);

/*------------------------------------define------------------------------------*/ 
    
/* 宏,用来做快速建立并添加操作. */
#define cJSON_AddNullToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b)	cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n)	cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s)	cJSON_AddItemToObject(object, name, cJSON_CreateString(s))

/* 当赋值一个整数的时候, 需要对浮点数也进行同时赋值. */
#define cJSON_SetIntValue(object,val)			\
((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#define cJSON_SetNumberValue(object,val)		\
((object)?(object)->valueint=(object)->valuedouble=(val):(val))

#ifdef __cplusplus
    
}//extern c
#endif

#endif

cJSON.c

/* cJSON */
/* JSON parser in C. */
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
#include "cJSON.h"

//静态全局指针,指针本身不需要释放,静态全局变量在数据段的data或者bss段(看是否初始化)
static const char *ep;

//返回出错指针,也就是返回错误信息
const char *cJSON_GetErrorPtr(void) 
{
	return ep;
}

//没看懂
/*忽略大小写比较字符串s1和s2, 参数使用const进行修饰,说明内部不会修改这两个值。
 *static说明文件内作用域,返回整数,
 *=0 - 相等;
 *>0 - s1>s2;
 *<0 - s1<s2;*/
static int cJSON_strcasecmp(const char *s1,const char *s2)
{
	//s1==NULL的情况下,如果s2也是NULL就相等,不然就是s1<s2;
	if (!s1) 
		return (s1==s2)?0:1;
	//s1不是NULL,但是s2是NULL,那么s1>s2
	if (!s2) 
		return 1;
    
	//不区分大小写,即都以小写形式进行比较,循环比较每个字符。不相等则跳出循环。
	for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)	
		if(*s1 == 0)//如果这个条件为真,说明s1==s2,s1==NULL。即,两个字串不区分大小写相同
			return 0;
	//将不相同的那个字符的小写形式进行相减,可以得到两个串的大小。
	//强制转换防止报错吧。
	return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}


//这里声明并且定义了一个指针函数指针(即cJSON_malloc是一个指针函数,这个指针函数指向的函数的返回值是void*指针类型,所以被称为指针函数指针,用来指向cjson中默认的分配内存的函数,这里是malloc)
//然后声明定义了一个函数指针cJSON_free,指向一个函数,即用来释放申请内存的函数,这里是free。
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;

//返回静态的char*类型字符串,传入一个字符串并且将这个字符串放到一段申请的内存空间中,然后返回这段空间的首地址。
static char* cJSON_strdup(const char* str)
{
      size_t len;
      char* copy;
	  //len是字符串的长度+\0。
      len = strlen(str) + 1;
	  //申请len+1长度的内存空间
      if ( !(copy = (char*)cJSON_malloc(len)) ) 
          return 0;
	  //申请len长的内存后用memcpy将字符串拷贝进去,然后返回首地址
      memcpy(copy,str,len);
      return copy;
}

//初始化申请和释放内存的方式,这个初始化钩子(回调)的函数也仅仅是调整申请内存的方式和释放接口的方式,默认情况下还是使用malloc和free。
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
    //传入为null空时,使用默认的方式申请和释放内存,即malloc和free。
    if (!hooks) 
    { /* Reset hooks */
        cJSON_malloc = malloc;
        cJSON_free = free;
        return;
    }
    
	//不为空时,那么就是传进来了hooks结构体,用来初始化不为malloc和free的内存申请和释放的方式,
    //我们将指针函数指针cJSON_malloc和指针函数cJSON_free重定向到hooks规定的内存申请和释放的函数。
	cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
	cJSON_free	 = (hooks->free_fn)?hooks->free_fn:free;
}

//将输入的num字符串按照json格式中的值的规则进行解析后,填充到对应的节点中的内存中,返回字符串被处理后结尾的位置的指针,解析num到item的cjson结构体中
static const char *parse_number(cJSON *item,const char *num)
{
	double n=0,sign=1,scale=0;//表示位默认为正
	int subscale=0,signsubscale=1;
	
    //判断字符串num是否为负数,是负数将标志位设置为-1并且指向下一位.
	if (*num=='-')
		sign=-1,num++;//这里num是char*指针而不是数字,所以num++的意思其实是指向下一位。
	//是否为0
	if (*num=='0') 
		num++;
    
	//若数字部分是十进制,将字符串转化为数字后存储在n内
	if (*num>='1' && *num<='9')
		do
			n=(n*10.0)+(*num++ -'0');	//*num ++ - ‘0’是利用ASCLL码来得出数字
    									//(*num++ -'0');相当于*num - ’0‘;*num++;
		while (*num>='0' && *num<='9');
    	//注意这里不代表着字符串已经被处理完了,若是小数,小鼠中的“.”也会导致这个循环结束。
    	//所以更确切的说,n现在存储的是整数或者小数的整数部分。
    
	//若存在小数点,将数字继续转存到n的末尾以整数方式,使用scale记录有几位小数
	if (*num=='.' && num[1]>='0' && num[1]<='9') 
	{
		num++;
		do
			n=(n*10.0)+(*num++ -'0'),scale--; 
		while (*num>='0' && *num<='9');
	}
    
	//如果是指数计数小数,记录指数符号,然后将数字进行转存到subscale中
	if (*num=='e' || *num=='E')
	{	
		num++;
		if (*num=='+')
			num++;
		else if (*num=='-') 
			signsubscale=-1,num++;	
		while (*num>='0' && *num<='9') 
			subscale=(subscale*10)+(*num++ - '0');
	}
	/* number = +/- number.fraction * 10^+/- exponent ,即根据指数计算出n的值*/
	n=sign*n*pow(10.0,(scale+subscale*signsubscale));
	//将计算到的值赋值到item中,int和double项都要赋值。类型赋值为NUM
	item->valuedouble=n;
	item->valueint=(int)n;
	item->type=cJSON_Number;
	return num;
}

//将一个32位整数扩展为比它大且最接近它的2的幂.
//原理是通过将最高位到最低位的二进制值全部设置为1.
static int pow2gt (int x)	
{
	--x;
	x|=x>>1;	x|=x>>2;	x|=x>>4;	x|=x>>8;	x|=x>>16;	
	return x+1;	
}

//定义一个printbuffer类型,主要是用来将json数据打印到缓冲区时,进行提供缓存空间的信息。
typedef struct 
{
	char *buffer; 		//缓冲区的首地址
	int length;   		//缓冲区的长度
	int offset;  		//缓冲区已经使用了的长度。
} printbuffer;


//用来确保缓冲区的内存是够用的,needed是需要的内存空间,内存够用时,会返回可用的空间的首地址,不够用时,会重新分配更大的内存空间并且更新printbuffer 结构体,同时返回可用的空间的首地址。
static char* ensure(printbuffer *p,int needed)
{
	char *newbuffer;
    int newsize;
	
    //传参判断
	if (!p || !p->buffer)
		return 0;
    
	//已经使用的内存空间+需要的内存空间
	needed+=p->offset;
    
	//缓冲区够用,返回当前缓存中最后一个可用的位置
	if (needed<=p->length) 
		return p->buffer+p->offset;
	
    //缓冲区不够用时
	//计算大于当前所需数目的最小2倍幂,用来分配内存数目
	newsize=pow2gt(needed);
	//申请内存并且初始化新的newbuffer结构体信息。
	newbuffer=(char*)cJSON_malloc(newsize);
	if (!newbuffer)
	{
		cJSON_free(p->buffer);
		p->length=0,
		p->buffer=0;
		return 0;
	}
	//拷贝原来的数据。
	if (newbuffer) 
		memcpy(newbuffer,p->buffer,p->length);
	//释放原指针,并更新nwebuffer缓存信息,然后返回缓存中第一个可用的内存位置。
	cJSON_free(p->buffer);
	p->length=newsize;
	p->buffer=newbuffer;
	return newbuffer+p->offset;
}

//返回缓冲区使用的内存偏移量
static int update(printbuffer *p)
{
	char *str;

	if (!p || !p->buffer) 
		return 0;
	//将str定义到新加入缓存的数据的首地址。然后使用strlen计算新添加长度后加上原有的偏移量进行返回。
	str=p->buffer+p->offset;
	return p->offset+strlen(str);
}

/*--------------------------------------------------------------------------------------*/
//打印cJSON结构体指针指向的结构体item中的数字(以字符串形式返回)
static char *print_number(cJSON *item,printbuffer *p)
{
	char *str=0;
	double d=item->valuedouble;
	//若值为零,根据p的值分配内存。
	if (d==0)
	{
		if (p)	str=ensure(p,2);
		else	str=(char*)cJSON_malloc(2);	/* special case for 0. */
		if (str) strcpy(str,"0");
	}
	
    //若值不为0
	//(fabs(((double)item->valueint)-d)<=DBL_EPSILON,标示差小于最小误差值,即可以理解为整数,并用		INT_MAX、INT_MIN,验证合法性数据。
	else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
	{
		if (p)	str=ensure(p,21);
		else	str=(char*)cJSON_malloc(21);	/* 2^64+1 can be represented in 21 chars. */
		if (str)	sprintf(str,"%d",item->valueint);
	}
	//else用来处理小数
	else
	{
		if (p)	str=ensure(p,64);
		else	str=(char*)cJSON_malloc(64);	/* This is a nice tradeoff. */
		if (str)
		{
			//如果小数值特别接近零,并且整数部分值特别大,那么就以xxxxx.0方式输出
			if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)
                sprintf(str,"%.0f",d);
			//如果数值比1.0e-6小或者比1.0e9数值大,那么比较适合用科学计数法标示
			else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9)			
                sprintf(str,"%e",d);
			//剩余部分直接用小数点形式进行输出
			else												
                sprintf(str,"%f",d);
		}
	}
	return str;
}

//16进制字符串转成无符号整数
static unsigned parse_hex4(const char *str)
{
	//原理就是将1——F逐个加上对应的值,四个位为一组进行处理。
	unsigned h=0;
	if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
	h=h<<4;str++;
	if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
	h=h<<4;str++;
	if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
	h=h<<4;str++;
	if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
	return h;
}

//对json字符串中的转义字符进行处理,返回转义后的字符串首地址
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static 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. */
					                                    //utf16和utf8之间格式的转换
					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;
}

/* Render the cstring provided to an escaped version that can be printed. */
//解析字符串,将转义字符串转义后的版本,返回解析后的字符串。
static char *print_string_ptr(const char *str,printbuffer *p)
{
	const char *ptr;
	char *ptr2,*out;
	int len=0,flag=0;
	unsigned char token;
	//测试str中是否携带着空格,引号,以及转义字符反斜杠,结果用flag进行标识
	for (ptr=str;*ptr;ptr++)
		flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0;
	//如果没有携带上诉的字符,那么根据p指针使用ensure检查内存或者执行分配内存,并进行内存检查。
	//然后将str中的字符串前后加上引号,存储到out所指向的内存中,并将地址进行返回。
	if (!flag)
	{
		len=ptr-str;
		if (p) 
			out=ensure(p,len+3);
		else		
			out=(char*)cJSON_malloc(len+3);
		if (!out)
			return 0;
		ptr2=out;*ptr2++='\"';
		strcpy(ptr2,str);
		ptr2[len]='\"';
		ptr2[len+1]=0;
		return out;
	}
	//如果str为NULL,那么就只填上一个双引号间填充空的打印到内存或者缓存。
	if (!str)
	{
		if (p)	out=ensure(p,3);
		else	out=(char*)cJSON_malloc(3);
		if (!out) return 0;
		strcpy(out,"\"\"");
		return out;
	}
	ptr=str;
	while ((token=*ptr) && ++len) 
	{
		if (strchr("\"\\\b\f\n\r\t",token))
			len++; 
		else if (token<32) 
			len+=5;ptr++;

	}
	
	if (p)	out=ensure(p,len+3);
	else	out=(char*)cJSON_malloc(len+3);
	if (!out) return 0;
	//就是转义字符的处理基本上是按照原样输出到输出结果中的。
	ptr2=out;ptr=str;
	*ptr2++='\"';
	while (*ptr)
	{
		if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
		else
		{
			*ptr2++='\\';
			switch (token=*ptr++)
			{
				case '\\':	*ptr2++='\\';	break;
				case '\"':	*ptr2++='\"';	break;
				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;
				default: sprintf(ptr2,"u%04x",token);ptr2+=5;	break;	/* escape and print */
			}
		}
	}
	*ptr2++='\"';*ptr2++=0;
	return out;
}

/* 使用一个对象调用 print_string_ptr (很有用的). */
/*将item中的valuestring打印到分配的内存中或者是缓存p中。局部作用域,返回输出值*/
static char *print_string(cJSON *item,printbuffer *p)	
{
    return print_string_ptr(item->valuestring,p);
}

/* 声明一些函数原型解析一个值,与打印一个值成对存在。. */
static const char *parse_value(cJSON *item,const char *value);
static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p);
static const char *parse_array(cJSON *item,const char *value);
static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p);
static const char *parse_object(cJSON *item,const char *value);
static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p);

//跳过ascii码中小于等于32的字符,即空字符或者一个控制字符。
static const char *skip(const char *in) 
{
	while (in && *in && (unsigned char)*in<=32)
		in++;
	return in;
}


//默认的解析方式(常用)
cJSON *cJSON_Parse(const char *value) 
{
	return cJSON_ParseWithOpts(value,0,0);
}

//打印CJSON中的文本调用
char *cJSON_Print(cJSON *item)				
{
    return print_value(item,0,1,0);
}

/* 打印无格式的cJSON到文本中调用. print_value*/
char *cJSON_PrintUnformatted(cJSON *item)	
{
    return print_value(item,0,0,0);
}

/* 打印cJSON到缓存中 调用. print_value*/
/*item为待解析打印的json数据,prebuffer为预分配到缓存的大小,fmt控制是否需要json格式*/
char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt)
{
	printbuffer p;
	p.buffer=(char*)cJSON_malloc(prebuffer);
	p.length=prebuffer;
	p.offset=0;
	return print_value(item,0,fmt,&p);
	return p.buffer;
}

/*重点!!!*/
/*解析器核心,遇到什么格式就进行什么格式的解析。*/
static const char *parse_value(cJSON *item,const char *value)
{
	if (!value)						
        return 0;	/* Fail on null. */
    
	//三种特定的数据类型,直接赋值item->type,并返回之后的数据。
	if (!strncmp(value,"null",4))	{ item->type=cJSON_NULL;  return value+4; }
	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; }
	//如果是引号开头的值传入,那么就进行解析字符串。
	if (*value=='\"')				
    { 
        return parse_string(item,value);
    }
	//解析数字
	if (*value=='-' || (*value>='0' && *value<='9'))	
    { 
        return parse_number(item,value); 
    }
	//解析数组,
	if (*value=='[')				
    { 
        return parse_array(item,value); 
    }
	//解析一个对象,递归着
	if (*value=='{')				
    { 
        return parse_object(item,value);
    }
	//如果到了这里,那么就置ep指针到出错的字串位置,然后返回0;
	ep=value;return 0;	/* failure. */
}

/*打印一个值到文本方式中. */
/*item为待打印的对象,depth 当前对象到根节点的深度  fmt 是否打印json格式, p为缓存入口*/
/*返回将item中数据组织成一个串的起始地址,也会被递归的调用,一般情况下*/
static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p)
{
	char *out=0;
	if (!item) return 0;
	if (p)
	{//使用缓存模式进行打印,验证类型进入不同的打印方式cJSON_Object,cJSON_Array会递归调用的
		switch ((item->type)&255)
		{
			case cJSON_NULL:	{out=ensure(p,5);	if (out) strcpy(out,"null");	break;}
			case cJSON_False:	{out=ensure(p,6);	if (out) strcpy(out,"false");	break;}
			case cJSON_True:	{out=ensure(p,5);	if (out) strcpy(out,"true");	break;}
			case cJSON_Number:	out=print_number(item,p);break;
			case cJSON_String:	out=print_string(item,p);break;
			case cJSON_Array:	out=print_array(item,depth,fmt,p);break;
			case cJSON_Object:	out=print_object(item,depth,fmt,p);break;
		}
	}
	else
	{
		switch ((item->type)&255)
		{//不适用缓存方式进行打印值,cJSON_Object,cJSON_Array会递归调用的
			case cJSON_NULL:	out=cJSON_strdup("null");	break;
			case cJSON_False:	out=cJSON_strdup("false");break;
			case cJSON_True:	out=cJSON_strdup("true"); break;
			case cJSON_Number:	out=print_number(item,0);break;
			case cJSON_String:	out=print_string(item,0);break;
			case cJSON_Array:	out=print_array(item,depth,fmt,0);break;
			case cJSON_Object:	out=print_object(item,depth,fmt,0);break;
		}
	}
	return out;
}



/* 根据输入的文本,建立一个数组 */
static const char *parse_array(cJSON *item,const char *value)
{
	cJSON *child;
	if (*value!='[')	{ep=value;return 0;}	/* not an array! */
	//验证为数组的value,对类型进行赋值,对value进行去除不可见字符,并判断空对象数组。
	item->type=cJSON_Array;
	value=skip(value+1);
	if (*value==']') return value+1;	/* empty array. */
	//为数组建立一个孩子节点。并检查内存分配,跳过不可见字符,并调用parse_value取得值
	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 */
	}
	//检查是否存在数组结束的右括号,然后返回结束位置,或者置位错误指向出错位置,然后返回0
	if (*value==']') return value+1;	/* end of array */
	ep=value;return 0;	/* malformed. */
}


/* 将对象数组打印成文本 */
static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p)
{
	char **entries;
	char *out=0,*ptr,*ret;int len=5;
	//获得数组的孩子,即第一个元素
	cJSON *child=item->child;
	int numentries=0,i=0,fail=0;
	size_t tmplen=0;
	//计算有多少个元素在这个数组里。
	/* How many entries in the array? */
	while (child) numentries++,child=child->next;
	/*如果这个数组为空,那么就打印一个[]出来就好了。*/
	if (!numentries)
	{
		if (p)	out=ensure(p,3);
		else	out=(char*)cJSON_malloc(3);
		if (out) strcpy(out,"[]");
		return out;
	}
	//如果是以缓存方式打印出来的话进这个分支
	if (p)
	{
		/* Compose the output array. */
		//先将[括号写进缓存中
		i=p->offset;
		ptr=ensure(p,1);if (!ptr) return 0;	*ptr='[';	p->offset++;
		//从第一个孩子开始进行遍历
		child=item->child;
		while (child && !fail)
		{	//打印这个孩子值到缓存中,并更新缓存中offset值。
			print_value(child,depth+1,fmt,p);
			p->offset=update(p);
			//判断是否需要格式打印,并根据此进行分配空间,格式化的会在有空格符号插入
			if (child->next) 
			{
				len=fmt?2:1;ptr=ensure(p,len+1);
				if (!ptr) 
					return 0;
				*ptr++=',';
				if(fmt)
					*ptr++=' ';
				*ptr=0;
				p->offset+=len;

			}
			//遍历传递
			child=child->next;
		}
		//输出右括号,并将out指向这次填充的最开始处
		ptr=ensure(p,2);if (!ptr) return 0;	*ptr++=']';*ptr=0;
		out=(p->buffer)+i;
	}
	else
	{//不使用缓存,那么就根据元素个数申请二维字符指针,并检查内存申请,初始化指针为NULL。
		/* Allocate an array to hold the values for each */
		entries=(char**)cJSON_malloc(numentries*sizeof(char*));
		if (!entries) return 0;
		memset(entries,0,numentries*sizeof(char*));
		/* 遍历所有的元素 */
		child=item->child;
		while (child && !fail)
		{
			//使用中间变量进行遍历并将结果全都放入二维指针中。
			ret=print_value(child,depth+1,fmt,0);
			entries[i++]=ret;
			//判断是否解析值出错。并计算长度
			if (ret) 
				len+=strlen(ret)+2+(fmt?1:0); 
			else 
				fail=1;
			child=child->next;
		}
		//如果没有解析错误,那么尝试分配一个输出的数组。
		/* If we didn't fail, try to malloc the output string */
		if (!fail)	out=(char*)cJSON_malloc(len);
		/* If that fails, we fail. */
		//如果分配失败,那么这次打印就失败了。
		if (!out) fail=1;

		/* 处理错误情况,将之前申请的所有内存进行释放*/
		if (fail)
		{
			for (i=0;i<numentries;i++) 
				if (entries[i]) 
					cJSON_free(entries[i]);
			cJSON_free(entries);
			return 0;
		}
		//没有错误情况,那么就将所有的字符串全都复制到新开辟的大的串中,准备输出。
		/* Compose the output array. */
		*out='[';
		ptr=out+1;*ptr=0;
		for (i=0;i<numentries;i++)
		{
			tmplen=strlen(entries[i]);memcpy(ptr,entries[i],tmplen);ptr+=tmplen;
			if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
			cJSON_free(entries[i]);
		}
		cJSON_free(entries);
		*ptr++=']';*ptr++=0;
	}
	return out;	
}

/* 根据文本输入,创建一个json对象. */
static const char *parse_object(cJSON *item,const char *value)
{
	cJSON *child;
	if (*value!='{')	{ep=value;return 0;}	/* 文本不是对象格式 */
	//设置类型,跳过不可见字符,并检查是否为空的数组
	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;
	//使用child->valuestring获得待解析的字符串,然后将child->valuestring的值给child->string
	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! */
	//将:后的值获取出来赋值给child,并且指针移到获取该值后字串的第一个可见字符处
	value=skip(parse_value(child,skip(value+1)));	/* skip any spacing, get the value. */
	if (!value) return 0;
	//如果存在,号 那么说明后续还要继续进行解析。并进行循环。
	while (*value==',')
	{//创建对象,并链接到链表中,然后进行解析值。如果出现了object,那么还是要递归调用。
		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;
	}
	//检查是否出现结束的右大括号。返回最后一个值结束的位置,或者返回0并置位ep
	if (*value=='}') return value+1;	/* end of array */
	ep=value;return 0;	/* malformed. */
}

/* 将一个对象,打印到文本中 */
static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p)
{
	char **entries=0,**names=0;
	char *out=0,*ptr,*ret,*str;
	int len=7,i=0,j;
	//获取根节点的孩子节点
	cJSON *child=item->child;
	int numentries=0,fail=0;
	size_t tmplen=0;
	/* Count the number of entries. */
	//计算其内包含的节点个数
	while (child) numentries++,child=child->next;
	/* Explicitly handle empty object case */
	//如果是个空的json对象,那么就打印一个空的花括号对。根据是否有格式选择转义字符
	//根据p决定将这个字符串输出的位置。
	//这里depth可以计算应该缩进的字符数
	if (!numentries)
	{
		if (p) out=ensure(p,fmt?depth+4:3);
		else	out=(char*)cJSON_malloc(fmt?depth+4:3);
		if (!out)	return 0;
		ptr=out;*ptr++='{';
		if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
		*ptr++='}';*ptr++=0;
		return out;
	}
	//如果是要求打印到缓存中去,那么进入这个处理逻辑
	if (p)
	{
		/* Compose the output: */

		//计算内训需求,将左括号和换行符输出到缓存中
		i=p->offset;
		len=fmt?2:1;	ptr=ensure(p,len+1);	if (!ptr) return 0;
		*ptr++='{';	if (fmt) *ptr++='\n';	*ptr=0;	p->offset+=len;
		//遍历孩子节点的子节点
		child=item->child;depth++;
		while (child)
		{//fmt格式输出,那么先打印应该输入的缩进再说。
			if (fmt)
			{
				ptr=ensure(p,depth);	if (!ptr) return 0;
				for (j=0;j<depth;j++) *ptr++='\t';
				p->offset+=depth;
			}
			//打印字符串到缓存中,并更新offset值
			print_string_ptr(child->string,p);
			p->offset=update(p);
			//处理冒号和格式
			len=fmt?2:1;
			ptr=ensure(p,len);	if (!ptr) return 0;
			*ptr++=':';if (fmt) *ptr++='\t';
			p->offset+=len;
			//将值解析出来放到p指向的缓存中,然后更新offset
			print_value(child,depth,fmt,p);
			p->offset=update(p);
			//计算长度,确保内存容量,检查是否还有后续节点,然后换行后进行下一个节点的遍历
			len=(fmt?1:0)+(child->next?1:0);
			ptr=ensure(p,len+1); if (!ptr) return 0;
			if (child->next) *ptr++=',';
			if (fmt) *ptr++='\n';*ptr=0;
			p->offset+=len;
			child=child->next;
		}
		//将右括号合上
		ptr=ensure(p,fmt?(depth+1):2);	 if (!ptr) return 0;
		if (fmt)	for (i=0;i<depth-1;i++) *ptr++='\t';
		*ptr++='}';*ptr=0;
		out=(p->buffer)+i;
	}
	else
	{//不使用缓存的情况下,先分配二维字符指针出来,并检查内存。初始化为0
		/* Allocate space for the names and the objects */
		entries=(char**)cJSON_malloc(numentries*sizeof(char*));
		if (!entries) return 0;
		names=(char**)cJSON_malloc(numentries*sizeof(char*));
		if (!names) {cJSON_free(entries);return 0;}
		memset(entries,0,sizeof(char*)*numentries);
		memset(names,0,sizeof(char*)*numentries);

		/* Collect all the results into our arrays: */
		//循环递归将所有的值都挂载在二维数组上
		child=item->child;depth++;if (fmt) len+=depth;
		while (child)
		{
			names[i]=str=print_string_ptr(child->string,0);
			entries[i++]=ret=print_value(child,depth,fmt,0);
			if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
			child=child->next;
		}
		
		/* Try to allocate the output string */
		//申请一个总的输出字串数组,检查内存
		if (!fail)	out=(char*)cJSON_malloc(len);
		if (!out) fail=1;

		/* Handle failure */
		//分配失败,那么将所有的已分配的内存均进行释放
		if (fail)
		{
			for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
			cJSON_free(names);cJSON_free(entries);
			return 0;
		}
		//将打印到各个子内存中的数据都拷贝一份到要返回的大内存中。并将原有的子内存进行释放
		/* Compose the output: */
		*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
		for (i=0;i<numentries;i++)
		{
			if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
			tmplen=strlen(names[i]);memcpy(ptr,names[i],tmplen);ptr+=tmplen;
			*ptr++=':';if (fmt) *ptr++='\t';
			strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
			if (i!=numentries-1) *ptr++=',';
			if (fmt) *ptr++='\n';*ptr=0;
			cJSON_free(names[i]);cJSON_free(entries[i]);
		}
		//补齐右括号
		cJSON_free(names);cJSON_free(entries);
		if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
		*ptr++='}';*ptr++=0;
	}
	return out;	
}


//获取数组的元素个数
int    cJSON_GetArraySize(cJSON *array)
{
	cJSON *c=array->child;
	int i=0;
	while(c)
		i++,c=c->next;
	return i;

}
//获取array中第item个元素的入口
cJSON *cJSON_GetArrayItem(cJSON *array,int item)
{
	cJSON *c=array->child; 
	while (c && item>0) 
		item--,c=c->next; 
	return c;

}
//获取对象object中名字为string的item。
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string)	
{
	cJSON *c=object->child;
	while (c && cJSON_strcasecmp(c->string,string))
		c=c->next;
	return c;
}

/* Utility for handling references. */
//创建一个参照对象,复制一个item,新item的type为cJSON_IsReference
static cJSON *create_reference(cJSON *item)
{
	cJSON *ref=cJSON_New_Item();
	if (!ref)
		return 0;
	memcpy(ref,item,sizeof(cJSON));
	ref->string=0;
	ref->type|=cJSON_IsReference;
	ref->next=ref->prev=0;
	return ref;

}

–create–

extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);

//初始化一个cJSON的结构体。并且为其申请相应的内存
static cJSON *cJSON_New_Item(void)
{
    //malloc了cJSON大小的内存返回首地址。
	cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
	//若成功申请了这片内存,初始化整个cJSON结构体为0
    if (node)
		memset(node,0,sizeof(cJSON));
	return node;
}
/*----------------------------------------------------------------------------------------*/
//创建基本类型的对象object,都是利用cJSON_New_Item()函数实现的,只不过再次指定了一下type的类型

//创建一个NULL类型的cjson对象object
cJSON *cJSON_CreateNull(void)					    
{
    cJSON *item=cJSON_New_Item();
    if(item)item->type=cJSON_NULL;
    return item;
}
//创建一个True类型的cjson对象object
cJSON *cJSON_CreateTrue(void)				
{
    cJSON *item=cJSON_New_Item();
    if(item)item->type=cJSON_True;
    return item;
}
//创建一个False类型的cjson对象object
cJSON *cJSON_CreateFalse(void)				
{
    cJSON *item=cJSON_New_Item();
    if(item)item->type=cJSON_False;
    return item;
}
//创建一个Bool类型的cjson对象object,然后根据b的值决定item->type是等于cJSON_True还是cJSON_False
cJSON *cJSON_CreateBool(int b)		
{
    cJSON *item=cJSON_New_Item();
    if(item)item->type=b?cJSON_True:cJSON_False;
    return item;
}
//创建一个cJSON_Number类型的cjson对象object,其值为num
cJSON *cJSON_CreateNumber(double num)		
{
    cJSON *item=cJSON_New_Item();
    if(item)
    {
        item->type=cJSON_Number;
        item->valuedouble=num;
        item->valueint=(int)num;
    }
    return item;
}
//创建一个cJSON_String类型的cjson对象object,其值为string
cJSON *cJSON_CreateString(const char *string)	
{
    cJSON *item=cJSON_New_Item();
    if(item)
    {
        item->type=cJSON_String;
        item->valuestring=cJSON_strdup(string);
    }
    return item;
}
//创建一个cJSON_Array类型的cjson对象object
cJSON *cJSON_CreateArray(void)					
{
    cJSON *item=cJSON_New_Item();
    if(item)item->type=cJSON_Array;
    return item;
}
//创建一个cJSON_Object类型的cjson对象object
cJSON *cJSON_CreateObject(void)				
{
    cJSON *item=cJSON_New_Item();
    if(item)item->type=cJSON_Object;
    return item;
}

extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);

//将item链接到prev之后
static void suffix_object(cJSON *prev,cJSON *item) 
{
    prev->next=item;
    item->prev=prev;
}
/*----------------------------------------------------------------------------------------*/
/*创建数组的函数,都是用cJSON_CreateNumber,cJSON_Createstring等和suffix_object实现的,只不过分开封装以方便以创建不同的json数组类型*/

//创建一个int类型的array,array中有count个元素,值分别为number
cJSON *cJSON_CreateIntArray(const int *numbers,int count)		
{
	int i;
	cJSON *n=0,*p=0,*a=cJSON_CreateArray();//创建一个array
	for(i=0;a && i<count;i++)
	{//创建number类型的对象,并赋值
		n=cJSON_CreateNumber(numbers[i]);
		if(!i)//第一个元素为孩子节点
			a->child=n;
		else //其他为兄弟节点挂接方式
			suffix_object(p,n);
		p=n;
	}
	return a;
}

//创建一个float类型的数组,数组中有count个元素,值分别为number
cJSON *cJSON_CreateFloatArray(const float *numbers,int count)	
{
    int i;
    cJSON *n=0,*p=0,*a=cJSON_CreateArray();
    for(i=0;a && i<count;i++)
    {
        n=cJSON_CreateNumber(numbers[i]);
        if(!i)a->child=n;
        else suffix_object(p,n);p=n;
    }
    return a;
}
//创建一个double类型的数组,数组中有count个元素,值分别为number
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count)	
{
    int i;
    cJSON *n=0,*p=0,*a=cJSON_CreateArray();
    for(i=0;a && i<count;i++)
    {
        n=cJSON_CreateNumber(numbers[i]);
        if(!i)a->child=n;
        else suffix_object(p,n);
        p=n;
    }
    return a;
}
//创建一个string类型的数组,数组中有count个元素,值分别为number
cJSON *cJSON_CreateStringArray(const char **strings,int count)	
{
    int i;
    cJSON *n=0,*p=0,*a=cJSON_CreateArray();
    for(i=0;a && i<count;i++)
    {
        n=cJSON_CreateString(strings[i]);
        if(!i)a->child=n;
        else suffix_object(p,n);
        p=n;
    }
    return a;
}

–add–

extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item);
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);

//将item链接到prev之后
static void suffix_object(cJSON *prev,cJSON *item) 
{
    prev->next=item;
    item->prev=prev;
}
/*----------------------------------------------------------------------------------------*/
//添加cjson结构体到某个数组或者对象之中

//将item json结构体添加到array数组中
void   cJSON_AddItemToArray(cJSON *array, cJSON *item)		
{
	cJSON *c=array->child;//获得array的第一个节点
    
	if (!item)
		return; 
    
	if (!c)
	{
        array->child=item;
    } 
	else
	{   //找到最后的节点并且挂上item
		while (c && c->next) 
			c=c->next; 
        
		suffix_object(c,item);
	}
}

//将一个名字为string的item添加到object中
void   cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item)	
{
	if (!item) 
        return; 
    
	if (item->string) //清理原有的名字
		cJSON_free(item->string);
    
	item->string=cJSON_strdup(string);
	cJSON_AddItemToArray(object,item);//添加并列的object和添加array其实在数据结构上是一样的
}

//将一个名字为string的item添加到object中,设置type为cJSON_StringIsConst
void   cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item)
{
	if (!item) 
        return; 
    
	if (!(item->type&cJSON_StringIsConst) && item->string) 
		cJSON_free(item->string);
    
	item->string=(char*)string;
	item->type|=cJSON_StringIsConst;
	cJSON_AddItemToArray(object,item);
}

//复制一个item,然后把复制的添加到array中
void	cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)	
{
    cJSON_AddItemToArray(array,create_reference(item));
}

//复制一个名字为string的item,然后把复制的添加到object中
void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item)
{
    cJSON_AddItemToObject(object,string,create_reference(item));
}

–delect—

extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);

//删除(释放)所有的cJSON结构体内存,并且删除(释放)它所有的子项的内存。
//cJSON_IsReference是宏定义=256
void cJSON_Delete(cJSON *c)
{
	cJSON *next;
	while (c)
	{
		//记住cJSON结构体指针类型的形参c的next指针
		next=c->next;
		//c的type类型是是用宏定义的,0,1,2,3,4,5,6,他们与1 0000 0000 (256)进行与&
        //运算,若其有类型的定义,那么!(c->type&cJSON_IsReference)便为真,代表这个节点是有值的。
        //个人认为这里使用!(c->type&cJSON_IsReference)做条件判断,而不是直接用c->type来做判断
        //是为了使得type为false类型(即值为0)时,也能使得条件为真。
        //c->clild,用来判断孩子节点,如果还有孩子节点,那么就一直递归调用自己,直到其没有孩子节点了
		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节点的函数


//将array中的第which个item从array中摘取下来,作为返回值。
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which)		
{
	cJSON *c=array->child;
    
	while (c && which>0)
		c=c->next,which--;
	if (!c) 
        return 0;
	if (c->prev)
		c->prev->next=c->next;
	if (c->next) 
		c->next->prev=c->prev;
	if (c==array->child) //如果是第一个孩子节点
		array->child=c->next;
	c->prev=c->next=0;
	return c;

}

//从array中删除第which个元素
void   cJSON_DeleteItemFromArray(cJSON *array,int which)	
{
    cJSON_Delete(cJSON_DetachItemFromArray(array,which));
}

//从object中摘除名字为string的item,返回这个item
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) 
{
	int i=0;
	cJSON *c=object->child;
	while (c && cJSON_strcasecmp(c->string,string)) 
		i++,c=c->next;//遍历找到这个string名字的item
	if (c)//找到了就从object中把它移除。
		return cJSON_DetachItemFromArray(object,i);
	return 0;
}

//将指定名字string的item从object中删除
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) 
{
    cJSON_Delete(cJSON_DetachItemFromObject(object,string));
}

–updata and copy—

extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);

extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
extern void cJSON_Minify(char *json);

/*----------------------------------------------------------------------------------------*/
/*复制,添加的函数,以及解析器核心*/

/* 插入一个newitem到第which个位置,如果不存在这个位置,那么就随意加到一个位置*/
void   cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem)
{
	cJSON *c=array->child;
	while (c && which>0)
		c=c->next,which--;
	if (!c)
	{//如果遍历完或者which个,没有找到有效的item,那么就将newitem插入到array中
		cJSON_AddItemToArray(array,newitem);
		return;
	}
	//将newitem挂载节点c的前面
	newitem->next=c;
	newitem->prev=c->prev;
	c->prev=newitem;
	if (c==array->child) //为第一个节点
		array->child=newitem;
	else
		newitem->prev->next=newitem;

}

void   cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem)	
{
	cJSON *c=array->child;
	while (c && which>0)
		c=c->next,which--;
	if (!c)//找不到第which个item
		return;
	//将newitem插入到第which个item个位置,将原有的item摘下来(c)。
	newitem->next=c->next;
	newitem->prev=c->prev;
	if (newitem->next)
		newitem->next->prev=newitem;
	if (c==array->child)
		array->child=newitem; 
	else 
		newitem->prev->next=newitem;
	c->next=c->prev=0;
	cJSON_Delete(c);//将摘下来的c节点清除掉

}

//将object中名字为string的item替换为newitem
void   cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem)
{
	int i=0;
	cJSON *c=object->child;
	while(c && cJSON_strcasecmp(c->string,string))
		i++,c=c->next;//找到名字为string的这个item
	if(c)
	{
		newitem->string=cJSON_strdup(string);//需要重新分配一个内存作为名字string的值
		cJSON_ReplaceItemInArray(object,i,newitem);//插入到前文找到的第i个位置
	}

}

//用来复制一个cjson结构体,第二个recurse决定是否递归的复制每一个孩子节点,返回复制的节点的首地址
cJSON *cJSON_Duplicate(cJSON *item,int recurse)
{
	cJSON *newitem,*cptr,*nptr=0,*newchild;

	if (!item) 
        return 0;

	//创建新的cjson
	newitem=cJSON_New_Item();
	if (!newitem) 
        return 0;

	//复制到新的数组中
	newitem->type=item->type&(~cJSON_IsReference), 			\
    newitem->valueint=item->valueint,						\
    newitem->valuedouble=item->valuedouble;			
    
    //若是string类型,也将string的值复制到新json结构体中
	if (item->valuestring)	
	{
		newitem->valuestring=cJSON_strdup(item->valuestring);	
		if (!newitem->valuestring)//若失败,删除节点返回
		{
			cJSON_Delete(newitem);
			return 0;
		}
	}
    
    //复制旧的结构体的名字
	if (item->string)		
	{
		newitem->string=cJSON_strdup(item->string);	
		if (!newitem->string)		
		{
            cJSON_Delete(newitem);
            return 0;
        }
	}

	//判断是否需要递归的进行复制子节点,0不用,其他值即需要。
	if (!recurse) 
        return newitem;

	//走到子节点上,之后遍历进行复制
	cptr=item->child;
	while (cptr)
	{
		newchild=cJSON_Duplicate(cptr,1);//递归自己,进行复制	
		if (!newchild)
        {
            cJSON_Delete(newitem);
            return 0;
        }
		if (nptr)	
		{
            nptr->next=newchild,newchild->prev=nptr;
            nptr=newchild;
        }/* 是兄弟节点则挂链*/
		else		
		{
            newitem->child=newchild;
            nptr=newchild;
        }/* 是孩子节点则设置指针 */
		cptr=cptr->next;
	}
	return newitem;
}

//解析一个对象并且创建cjson格式的数据结构。
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));
	//如果返回值为NULL,说明解析不成功,删除新创建的节点。
	if (!end)
	{
		cJSON_Delete(c);
		return 0;
	}	/*如果解析失败,ep已经被指向了错误原因了。 */

	/* 如果我们要求以NULL结尾,那么检测是否以NULL进行结尾的。不然就释放内存并将ep指向出错的位置*/
	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;
}

//一个mini版本的json数据的遍历功能
void cJSON_Minify(char *json)
{
	char *into=json;
    
	while (*json)
	{
		if (*json==' ') json++;
        
		else if (*json=='\t') json++;	/* Whitespace characters. */
		else if (*json=='\r') json++;
		else if (*json=='\n') json++;
		else if (*json=='/' && json[1]=='/')  
            
         while (*json && *json!='\n') json++;	
        /* double-slash comments, to end of line. */
        
		else if (*json=='/' && json[1]=='*')
        {
            while (*json && !(*json=='*' && json[1]=='/')) 
                json++;json+=2;
        }	/* multiline comments. */
        else if (*json=='\"')
        {
            *into++=*json++;
            while (*json && *json!='\"')
            {
                if (*json=='\\') 
                	*into++=*json++;
                *into++=*json++;
            }
            *into++=*json++;
        } /* string literals, which are \" sensitive. */
		else *into++=*json++;			/* All other characters. */
	}
	*into=0;	/* and null-terminate. */
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/586185.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mongodb卸载(win)

关闭服务 &#xff08;或者cmd卸载服务&#xff1a;&#xff09; net stop 服务名称卸载应用 至此&#xff0c;卸载完成&#xff01;

微隔离实施五步法,让安全防护转起来

前言 零信任的最核心原则→最小权限 安全的第一性原理→预防 零信任的最佳实践→微隔离 “零信任”这个术语的正式出现&#xff0c;公认是在2010年由Forrester分析师John Kindervag最早提出。时至今日&#xff0c;“零信任”俨然已成安全领域最热门的词汇&#xff0c;做安全…

实验报告5-Spring MVC实现页面

实验报告5-SpringMVC实现页面 一、需求分析 使用Spring MVC框架&#xff0c;从视图、控制器和模型三方面实验动态页面。模拟实现用户登录&#xff0c;模拟的用户名密码以模型属性方式存放在Spring容器中&#xff0c;控制器相应用户请求并映射参数&#xff0c;页面收集用户数据或…

设计模式-01 设计模式单例模式

设计模式-01 设计模式单例模式 目录 设计模式-01 设计模式单例模式 1定义 2.内涵 3.使用示例 4.具体代码使用实践 5.注意事项 6.最佳实践 7.总结 1 定义 单例模式是一种设计模式&#xff0c;它确保一个类只能被实例化一次。它通过在类内部创建类的唯一实例并提供一个全…

uniapp + uView动态表单校验

项目需求&#xff1a;动态循环表单&#xff0c;并实现动态表单校验 页面&#xff1a; <u--form label-position"top" :model"tmForm" ref"tmForm" label-width"0px" :rulesrules><div v-for"(element, index) in tmForm…

(详细整理!!!!)Tensorflow与Keras、Python版本对应关系!!!

小伙伴们大家好&#xff0c;不知道大家有没有被tensorflow框架困扰过 今天我就给大家整理一下tensorflow和keras、python版本的对应关系 大家这些都可以在官网找到&#xff0c;下面我把官网的连接给大家放在这里&#xff1a;在 Windows 环境中从源代码构建 | TensorFlow (g…

搭建大型分布式服务(三十七)SpringBoot 整合多个kafka数据源-取消限定符

系列文章目录 文章目录 系列文章目录前言一、本文要点二、开发环境三、原项目四、修改项目五、测试一下五、小结 前言 本插件稳定运行上百个kafka项目&#xff0c;每天处理上亿级的数据的精简小插件&#xff0c;快速上手。 <dependency><groupId>io.github.vipjo…

基于 React 的图形验证码插件

react-captcha-code NPM 地址 &#xff1a; react-captcha-code - npm npm install react-captcha-code --save 如下我自己的封装&#xff1a; import Captcha from "react-captcha-code";type CaptchaType {captchaChange: (captchaInfo: string) > void;code…

前端发起网络请求的几种常见方式(XMLHttpRequest、FetchApi、jQueryAjax、Axios)

摘要 前端发起网络请求的几种常见方式包括&#xff1a; XMLHttpRequest (XHR)&#xff1a; 这是最传统和最常见的方式之一。它允许客户端与服务器进行异步通信。XHR API 提供了一个在后台发送 HTTP 请求和接收响应的机制&#xff0c;使得页面能够在不刷新的情况下更新部分内容…

Flutter笔记:Widgets Easier组件库(2)阴影盒子

Flutter笔记 Widgets Easier组件库&#xff08;2&#xff09;&#xff1a;阴影盒子 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress o…

Python中的动态数据可视化Bokeh库实战

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python 中的动态数据可视化Bokeh库实战 在数据科学和可视化领域&#xff0c;动态数据可视化…

windows下安装onlyoffice

文章目录 1、 安装ErLang2、 安装rabbitmq3、 安装postgresql4、 安装onlyoffice(社区版) 1、 安装ErLang 下载地址&#xff1a;https://erlang.org/download/otp_win64_24.2.exe opt_wind64_24.2.exe 直接运行&#xff0c;一步一步安装 2、 安装rabbitmq 下载地址&#xf…

【笔记】Simulink与Workbench交互+自定义m函数封装为Simulink模块

以如下三角函数为例&#xff0c;说明建模方法 ya*sin(b*2*pi*uc);0.总模型总代码 总模型 总代码&#xff1a; clc clear close allt_all10; a10; b1; c0;%pi/2; delta_t0.01; simOutsim(test240430); out_tsimOut.tout; out_y1simOut.yout{1}.Values; out_y2simOut.yout{2}.…

C++-10

1.C一个程序&#xff0c;实现两个类&#xff0c;分别存放输入的字符串中的数字和字母&#xff0c;并按各自的顺序排列&#xff0c; 类中实现-一个dump函数&#xff0c;调C用后输出类中当前存放的字符串结果。 例如&#xff0c;输入1u4y2a3d,输出:存放字母的类&#xff0c;输出a…

机器人正反向运动学(FK和IK)

绕第一个顶点可以沿Z轴转动&#xff0c;角度用alpha表示 绕第二个点沿X轴转动&#xff0c;角度为Beta 第三个点沿X轴转动&#xff0c;记作gama 这三个点构成姿态&#xff08;pose&#xff09; 我们记第一个点为P0&#xff0c;画出它的本地坐标系&#xff0c;和世界坐标系一样红…

无人机+三维建模:倾斜摄影技术详解

无人机倾斜摄影测量技术是一项高新技术&#xff0c;近年来在国际摄影测量领域得到了快速发展。这种技术通过从一个垂直和四个倾斜的五个不同视角同步采集影像&#xff0c;从而获取到丰富的建筑物顶面及侧视的高分辨率纹理。这种技术不仅能够真实地反映地物情况&#xff0c;还能…

设计模式 --6组合模式

文章目录 组合模式应用场景组合模式概念组合模式结构图透明方式和安全方式什么时候使用组合模式公司管理系统使用 组合模式来构架组合模式的好处 组合模式应用场景 整体和部分可以被一致性对待 比如人力资源部 财务部的管理功能可以复用于分公司的功能 可以引入一种 树状的结构…

【webrtc】MessageHandler 2: 基于线程的消息处理:以PeerConnectionClient为例

PeerConnectionClient 前一篇 nullaudiopoller 并么有场景线程,而是就是在当前线程直接执行的, PeerConnectionClient 作为一个独立的客户端,默认的是主线程。 PeerConnectionClient 同时维护客户端的信令状态,并且通过OnMessage实现MessageHandler 消息处理。 目前只处理一…

AI大模型日报#0430:疑似GPT4.5模型刷屏、上交实现「蛋白质功能定向进化」、微软紧急撤回WizardLM-2

导读&#xff1a; 欢迎阅读《AI大模型日报》&#xff0c;内容基于Python爬虫和LLM自动生成。目前采用“文心一言”生成了今日要点以及每条资讯的摘要。 《AI大模型日报》今日要点&#xff1a; 在AI大模型领域&#xff0c;多项研究进展和行业应用动态引发关注。一夜之间&#x…

Gateway Predicate断言(谓词)

是什么 Spring Cloud Gateway匹配路由作为Spring WebFlux HandlerMapping基础设施的一部分。 Spring Cloud Gateway包含许多内置的路由谓词工厂。 所有这些谓词都匹配HTTP请求的不同属性。 您可以使用逻辑 and 语句来联合收割机组合多个路由谓词工厂。 Predicate就是为了实现一…
最新文章