cJSON开源项目详细解剖

JSON

JSON介绍

百度百科:JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

JSON语法

JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名。JSON是一个序列化的对象或数组。

数组和对象(大括号包含的)有以key:value格式还可以有value格式的,
如下:
key:value格式

{
	"name"	: "John Doe", 
	"age"	: 	18, 
	"address": {
				"country" 	: "china",
				 "zip-code"	: "10000"
				}
}

value格式

[3, 1, 4, 1, 5, 9, 2, 6]

混合格式

[1, 2, "3", {
				"a": 4
			}
]

CJSON

源码下载及介绍

顾名思义就是用C语言实现JSON格式的解析器,可以将文本构造成JSON格式数据,也可以将JSON格式数据解析成文本。
下载地址:CJSON源码
下载好之后这是一个zip的压缩包,需要解压。下图就是解压后的所有文件
在这里插入图片描述
LICENSE文件和README文件是关于版权的介绍,建议带上就行。
cJSON.ccJSON.h是源码文件,test.c是一个测试文件,tests文件夹里面放一些JSON格式的数据以便测试使用。

CJSON思想

结构体

CJSON是使用双向链表存储数据,访问类型与树结构,所以我们先来了解这个结构体。

typedef struct cJSON
	{
		struct cJSON* next;	// 向后链表指针
		struct cJSON* prev;	// 向前链表指针
		struct cJSON* child;// 对象或者数组的孩子节点指针

		
		int type;					// value的类型

		char* valuestring;			// 字符串值
		int valueint;				// 整数值
		double valuedouble;			// 浮点数值 

		char* string;				// 存放key
	} cJSON;

type是值(value)的类型,一共有7种取值:
分别是:False,Ture,NULL,Number,String,Array,Object。

  1. Number类型,则valueint或valuedouble中存储着值。
  2. int类型,则valueint中存储着值
  3. double类型,则valuedouble中存储着值。
  4. String类型,则valuestring中存储着值。

画出CJSON结构

在这里插入图片描述

现在可以试着将上图的结构画出来。嵌套用child连接,兄弟关系用链表连接。
在这里插入图片描述

解析失败的措施

static const char* ep;
将会定位在解析失败的字符上,并将不能解析的字符串输出

函数解析

cJSON_GetErrorPtr

/*
作  用:字符串解析失败函数
返回值:返回全局变量ep
*/
const char* cJSON_GetErrorPtr(void)
{
	return ep;
}

cJSON_strcasecmp

/*
作	用:用来比较参数s1和s2字符串,比较时会自动忽略大小写的差异
参	数:s1、s2是需要比较字符串
返回值:0表示相同  其他数值表示不相同
*/
static int cJSON_strcasecmp(const char* s1, const char* s2)
{
	if (NULL == s1 && NULL == s2)						// s1均为null就返回0
	{
		return 0;
	}

	if (NULL == s1 || NULL == s2)						// 只要有一方为null就返回1
	{
		return 1;
	}

	for (; tolower(*s1) == tolower(*s2); ++s1, ++s2)	// tolower()大写转小写函数
	{
		if (*s1 == '\0')								// 发现全部匹配成功就返回0
		{
			return 0;
		}
	}
	return tolower(*(const unsigned char*)s1) - tolower(*(const unsigned char*)s2);
}

cJSON_strdup

/*
作	用:为str字符串在堆区开辟一块空间
参	数:字符串
返回值:堆区地址
*/
static char* cJSON_strdup(const char* str)
{
	size_t len = 0;										// size_t等价于unsigned int
	char* copy = NULL;									// 保存堆区开辟的地址

	len = strlen(str) + 1;								// 计算字符串长度 最后需要加个'\0' 所以长度+1
	copy = (char*)cJSON_malloc(len);					// 为字符串申请空间
	if (NULL == copy)									// 空间申请失败
	{
		return 0;
	}

	memcpy(copy, str, len);								// 将str复制到copy中,长度为len

	return copy;
}

cJSON_InitHooks

/* 初始化钩子 */
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
	if (NULL == hooks)
	{
		cJSON_malloc = malloc;
		cJSON_free = free;
		return;
	}

	cJSON_malloc = (hooks->malloc_fn) != NULL ? hooks->malloc_fn : malloc;
	cJSON_free = (hooks->free_fn) != NULL ? hooks->free_fn : free;
}

cJSON_New_Item

/*
作	用:为cJSON在堆区申请一块空间
返回值:堆区地址
*/
static cJSON* cJSON_New_Item(void)
{
	cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));	// 申请一个结构体的空间
	
	if (NULL == node)									// 申请空间失败
	{
		return NULL;
	}
	
	memset(node, 0, sizeof(cJSON));						// 初始化堆区开辟的内存
	return node;
}

cJSON_Delete

/*
作 用:删除一个cJSON结构
参 数:结构体地址
*/
void cJSON_Delete(cJSON* c)
{
	cJSON* next = NULL;

	while (NULL != c)
	{
		next = c->next;									// 指针向后移

		// 当前兄弟节点是最后一个节点且有孩子
		if (0 == (c->type & cJSON_IsReference) && NULL != c->child)
		{
			cJSON_Delete(c->child);
		}

		// 当前兄弟节点是最后一个且没有孩子,若有value为字符串就回收
		if (0 == (c->type & cJSON_IsReference) && NULL != c->valuestring)
		{
			cJSON_free(c->valuestring);
		}

		// 当前兄弟节点是最后一个且没有孩子,若有key也会受
		if (0 == (c->type & cJSON_StringIsConst) && NULL != c->string)
		{
			cJSON_free(c->string);
		}

		cJSON_free(c);									// 回收根节点
		c = NULL;
	}
}

parse_number

/*
作	用:将输入的字符串解析成数字(正数不需要+号,直接是数字,只有负数才有符号-)
参	数:item-结构体指针   num-字符串指针
返回值:字符串指针
*/
static const char* parse_number(cJSON* item, const char* num)
{	// 
	double n = 0;										// 保存数字
	double sign = 1;									// 数字符号的标志  1表示正数  -1表示负数
	double scale = 0;									// 用于有小数的数字,先不管小数点,记录扩大了多少倍
	int subscale = 0;									// 指数部分的数值值
	int signsubscale = 1;								// 指数部分的符号位

	if (*num == '-')									// 负数
	{
		sign = -1;										// 设置标记
		num++;											// 指针后移
	}

	if (*num == '0')									// 是0
	{
		num++;
	}

	/* 整数部分 */
	if (*num >= '1' && *num <= '9')						// 数字不能从0开始
	{
		do
		{
			n = (n * 10.0) + (*num - '0');
			num++;										// 指针后移
		} while (*num >= '0' && *num <= '9');			// 之后的数字范围就是0-9
	}

	/* 小数部分 */
	if (*num == '.' && num[1] >= '0' && num[1] <= '9')	// 当前字符为"."且下一个字符为数字才有效
	{
		num++;											// 跳过 小数点
		do
		{
			n = (n * 10.0) + (*num - '0');
			num++;
			scale++;									// 记录扩大了多少倍
		} while (*num >= '0' && *num <= '9');
	}

	/* 指数部分 */
	if (*num == 'e' || *num == 'E')
	{
		num++;											// 指针跳过指数

		if (*num == '+')								// 指数位的符号位
		{
			num++;
		}
		else if (*num == '-')
		{
			signsubscale = -1;							// 修改指数位的符号位
			num++;
		}

		while (*num >= '0' && *num <= '9')				// 指数部分的数字位  可以从0开始
		{
			subscale = (subscale * 10) + (*num++ - '0');
		}
	}

	// 将之前扩大的再缩回来 公式:数值符号位 * 数值位 * 10^(指数符号位 * 指数数值位 - 扩大倍数)
	n = sign * n * pow(10.0, (subscale * signsubscale - scale));

	item->valuedouble = n;								// 将该值存入double类型中
	item->valueint = (int)n;							// 将该值存入int类型中
	item->type = cJSON_Number;							// 将type类型设置为数字
	return num;
}

pow2gt

/* 作用:返回比x大的最小的2的N次方数 */
static int pow2gt(int x)
{
	--x;
	x |= x >> 1;
	x |= x >> 2;
	x |= x >> 4;
	x |= x >> 8;
	x |= x >> 16;

	return x + 1;
}

ensure

/*
作	用:判断当前偏移量下比较needed长度的字符是否越界并作出保护措施
参	数:p-输出缓冲结构体指针  needed-比较的长度
返回值:返回比较的首地址
*/
static char* ensure(printbuffer* p, int needed)
{
	char* newbuffer = NULL;
	int newsize = 0;

	if (NULL == p || NULL == p->buffer)		   // 结构体为空或者结构体缓冲区为空
	{
		return NULL;
	}

	needed += p->offset;						// 计算原本偏移量 + 新的比较长度

	if (needed <= p->length)					// 判断偏移量+比较长度是否小于字符串长度
	{
		return p->buffer + p->offset;			// 返回初始地址 + 偏移量
	}

	newsize = pow2gt(needed);					// 返回比x大的最小的2的N次方数
	newbuffer = (char*)cJSON_malloc(newsize);
	if (NULL == newbuffer)						// 空间申请失败
	{
		cJSON_free(p->buffer);
		p->length = 0;
		p->buffer = 0;
		return NULL;
	}

	memcpy(newbuffer, p->buffer, p->length);	// 将p->buffer拷贝到newbuffer,长度为参数三

	cJSON_free(p->buffer);						// 回收掉之前用于保存字符串的空间
	p->length = newsize;						// 更新输出缓冲区结构体
	p->buffer = newbuffer;						// 更新输出缓冲区结构体
	return newbuffer + p->offset;
}

update

/*
作	用:获取缓冲区的字符串长度(不是缓冲区大小,缓冲区大于等于字符串长度)
参	数:p-输出缓冲结构体指针
返回值:长度
*/
static int update(printbuffer* p)
{
	char* str = NULL;

	if (NULL == p || NULL == p->buffer)		   // 结构体为空或者结构体缓冲区为空
	{
		return 0;
	}

	str = p->buffer + p->offset;	
	return p->offset + strlen(str);			   // 偏移量 + 后半段长度
}

print_number

/*
作	用:将给的数字类型结构体转换为字符串存储到p缓冲区中,若p为null就申请空间
参	数:item-给的数字类型结构体  p-缓冲区指针
返回值:未解析的地址
*/
static char* print_number(cJSON* item, printbuffer* p)
{
	char* str = NULL;
	double d = item->valuedouble;
	if (0 == d)
	{
		if (NULL != p)						// 输出缓冲区结构体指针不为空  需要判断添加是否安全 
		{
			str = ensure(p, 2);				// 0转换成字符串 俩个字节  "0\0"
		}
		else
		{
			str = (char*)cJSON_malloc(2);	// 为0申请空间
			if (NULL == str)				// 申请失败
			{
				return NULL;
			}

		}
		strcpy(str, "0");					// 拷贝0字符串
	}
	else if (fabs(((double)item->valueint) - d) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) // 判断是整型
	{
		if (NULL != p)
		{
			str = ensure(p, 11);			// 2 ^ 32 + 1(-4294967296,4294967297)可以用11个字符表示。
		}
		else
		{
			str = (char*)cJSON_malloc(11);	// 为2 ^ 32 + 1 申请空间
			if (NULL == str)				// 申请失败
			{
				return NULL;
			}
		}
		sprintf(str, "%d", item->valueint);	// 将整型值存储到str指向的缓冲区中
	}
	else	// double类型
	{
		if (p != NULL)						// 2 ^ 64 + 1(18446744073709551617)可以用21个字符表示。
		{
			str = ensure(p, 21);
		}
		else
		{
			str = (char*)cJSON_malloc(21);
			if (NULL == str)
			{
				return NULL;
			}
		}

		if (fabs(floor(d) - d) <= DBL_EPSILON && fabs(d) < 1.0e60)	// 无小数的double类型数字
		{
			sprintf(str, "%.0f", d);
		}
		else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9)				// 范围在(1.0e-6,1.0e9)就用科学计数法
		{
			sprintf(str, "%e", d);
		}
		else														// 其他情况就用正常写法
		{
			sprintf(str, "%f", d);
		}	
	}
	return str;
}

parse_hex4

/* utf转换函数   */
static unsigned parse_hex4(const char* str)  
{
	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;
}

parse_string

/*
作	用:将字符串拷贝到堆区
参	数:item-结构体指针   str-字符串指针
返回值:字符串解析后的地址
*/
static const char* parse_string(cJSON* item, const char* str)
{
	const char* ptr = str + 1;			// *str为"字符,所以从下一个开始,遍历字符串的指针
	char* out;							// 用于接受申请在堆区空间的地址
	char* ptr2;							// 指向新开辟的空间的指针 ,用于将这段字符串拷贝在堆区
	int len = 0;						// 计算这段字符串的长度
	unsigned int uc, uc2;

	if (*str != '\"')					// 当*str不为"时发现解析错误
	{
		ep = str;
		return NULL;
	}

	/* 为了计算出这段字符串的长度 */
	while ('\"' != *ptr && '\0' != *ptr)	// 当前字符不能为"和\0
	{
		len++;							
		if (*ptr++ == '\\')					// 跳过转义字符 \  
		{
			ptr++;
		}
	}

	out = (char*)cJSON_malloc(len + 1);		// 给字符串开辟空间 多一个存放\0
	if (NULL == out)						// 空间申请失败
	{
		return NULL;
	}

	ptr = str + 1;							// 指针再回到字符串开始位置
	ptr2 = out;

	while ('\"' != *ptr && '\0' != *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':	 // 将utf16转换为utf8    不做分析
				uc = parse_hex4(ptr + 1);
				ptr += 4;	

				if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0)
					break;	

				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;
			}								   // 以上是utf16转utf8  不做分析
			ptr++;							   // 指向内存地址的指针后移
		}
	}

	*ptr2 = '\0';								// 拷贝完成最后补'\0'

	if (*ptr == '\"')							// 把字符串最后"跳过
		ptr++;

	item->valuestring = out;					// 将字符串给到结构体中的valuestring
	item->type = cJSON_String;					// 将数据类型设置为字符串
	return ptr;									// 返回新的字符串地址
}

print_string_ptr

/*
作	用:将字符串转换成文本模式
参	数:str-当前需要转义的字符串地址  p-输出缓冲区结构指针
返回值:返回该字符串在输出缓冲区起始地址
*/
static char* print_string_ptr(const char* str, printbuffer* p)
{
	const char* ptr = NULL;				// 用于遍历查找 和计算字符串长度
	char* ptr2 = NULL;					// 接受out值
	char* out = NULL;					// 获取输出缓冲区的地址
	int len = 0;						// 保存字符串长度
	int flag = 0;						// 1表示字符串包含控制字符或者"或者\  0表示正常字符
	unsigned char token = ' ';

	for (ptr = str; '\0' != *ptr; ptr++)// 判断子串中是否包含控制字符或者"或者\ 有则为1无则为0
	{
		flag |= ((*ptr > 0 && *ptr < 32) || (*ptr == '\"') || (*ptr == '\\')) ? 1 : 0;
		if (1 == flag)
			break;
	}

	if (0 == flag)						// 不包含控制字符 " \ 三种
	{
		len = ptr - str;				// 计算长度

		if (NULL != p)					// 输出缓冲区指针不为NULL
		{
			// 判断当前偏移量下比较len+3(因为需要放俩个""和一个\0)长度的字符是否越界并作出保护措施
			out = ensure(p, len + 3);
		}
		else
		{
			out = (char*)cJSON_malloc(len + 3);// 为字符串申请空间
		}

		if ('\0' == out)				// 空间申请失败
			return NULL;

		ptr2 = out;
		*ptr2++ = '\"';					// 先放第一个"
		strcpy(ptr2, str);				// 再拷贝字符串
		ptr2[len] = '\"';				// 最后放第二个"
		ptr2[len + 1] = '\0';			// 最后补\0
		return out;						// 返回该字符串在输出缓冲区起始地址
	}

	if (NULL == str)					// 字符串为NULL
	{
		if (NULL != p)					// 输出缓冲区指针不为NULL
		{
			// 判断当前偏移量下比较3(因为需要放俩个""和一个\0)长度的字符是否越界并作出保护措施
			out = ensure(p, 3);
		}
		else
		{
			out = (char*)cJSON_malloc(3);// 为字符串申请空间
		}

		if (NULL == out)				// 空间申请失败
			return NULL;
		strcpy(out, "\"\"");			// 想输出缓冲区放入 ""\0
		return out;						// 返回该字符串在输出缓冲区起始地址
	}
	ptr = str;
	while ('\0' != (token = *ptr) && ++len)// 计算字符串总长度
	{
		// 查找字符串 "\bfnrt 中首次出现字符token的位置,
		// 返回首次出现c的位置的指针,如果s中不存在c则返回NULL
		if (NULL != strchr("\"\\\b\f\n\r\t", token))
			len++;
		else if (token < 32)// 控制字符(不可见) 占5字节
			len += 5;

		ptr++;
	}

	if (NULL != p)						// 输出缓冲区指针不为NULL
	{
		// 判断当前偏移量下比较len + 3长度的字符是否越界并作出保护措施
		out = ensure(p, len + 3);
	}
	else
	{
		out = (char*)cJSON_malloc(len + 3);	// 为字符串申请空间
	}

	if (NULL == out)					// 空间申请失败
	{
		return NULL;
	}

	ptr2 = out;
	ptr = str;
	*ptr2++ = '\"';						// 存入第一个"
	while ('\0' != *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';						// 存入最后的\0
	return out;							// 返回该字符串在输出缓冲区起始地址
}

print_string

/* 向缓冲区存入字符串*/
static char* print_string(cJSON* item, printbuffer* p)
{
	return print_string_ptr(item->valuestring, p);							
}

skip

/* 跳过控制字符和空格(ASCII-32) */
static const char* skip(const char* in)
{
	while (in != NULL && *in != '\0' && (unsigned char)*in <= 32)	// 小于32是控制字符 32是空格
	{
		in++;	// 指针后移
	}
	return in;
}

cJSON_ParseWithOpts

/* 检查JSON 是否为空终止,并检索指向解析的最终字节的指针。
	在return_parse_end中提供ptr并且解析失败 */
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 (NULL == c)						// 申请内存失败
	{
		return NULL;
	}

	end = parse_value(c, skip(value));	// 跳过控制字符和空格,再处理文本
	if (NULL == end)					// 发现没有数据  解析失败
	{
		cJSON_Delete(c);// 释放申请的空间
		return NULL;
	}

	// 如果我们要求没有附加垃圾的空终止JSON,跳过然后检查空终止
	if (require_null_terminated != '\0')
	{
		end = skip(end);				// 跳过空格
		if ('\0' != *end)				// 遇到'\0'
		{
			cJSON_Delete(c);			// 回收
			ep = end;
			return NULL;
		}
	}

	if (return_parse_end != NULL)
	{
		*return_parse_end = end;
	}

	return c;
}

cJSON_Parse

/* cJSON解析默认函数 */
cJSON* cJSON_Parse(const char* value)
{
	return cJSON_ParseWithOpts(value, 0, 0);
}

cJSON_Print

/* 将cJSON格式的结构转成文本格式 */
char* cJSON_Print(cJSON* item)
{
	return print_value(item, 0, 1, NULL);
}

cJSON_PrintUnformatted

/* 将cJSON格式的结构转成文本格式(无格式) */
char* cJSON_PrintUnformatted(cJSON* item)
{
	return print_value(item, 0, 0, 0);
}

cJSON_PrintBuffered

/* 将cJSON格式的结构存储到输出缓冲区 */
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;
}

parse_value

/* 核心解析器——当遇到文本时,处理。 */
static const char* parse_value(cJSON* item, const char* value)
{
	if (value == NULL)// 不能为空。
	{
		return 0;
	}

	// strncmp字符串比较函数,第三个参数为比较长度

	if (strncmp(value, "NULL", 4) == 0)// 判断是否为NULL 然后跳过4个字节
	{
		item->type = cJSON_NULL;
		return value + 4;// 指针后移
	}

	if (strncmp(value, "false", 5) == 0)// 判断是否为false 然后跳过5个字节
	{
		item->type = cJSON_False;
		return value + 5;
	}

	if (strncmp(value, "true", 4) == 0)// 判断是否为true 然后跳过4个字节
	{
		item->type = cJSON_True;
		return value + 4;
	}

	if (*value == '\"')// 判断是否为"
	{
		// 将当前以"开头"结束字符串一段存入结构体,并设置type。返回新的地址
		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。
	ep = value;
	return 0;
}

print_value

/*
功	能:将json结构体转换为字符串。
参	数:item-json结构体指针   depth-深度  fmt-输出格式  p-输出缓冲区结构体指针
返回值:字符串首地址
*/
static char* print_value(cJSON* item, int depth, int fmt, printbuffer* p)
{
	char* out = NULL;
	if (NULL == item)					// 结构体指针为NULL
	{
		return NULL;
	}

	if (NULL != p)						// 输出缓冲结构指针不为NULL
	{
		switch (item->type)
		{
		case cJSON_NULL:				// 输出缓冲区指针不为NULL且类型为null 直接在原始地址偏移量下拷贝null
		{
			out = ensure(p, 5);			// 检查当前偏移量下还能否放进去5个字符 "null\0" 并返回偏移后的地址
			if (out != NULL)
				strcpy(out, "null");
			break;
		}
		case cJSON_False:				// 输出缓冲区指针不为NULL且类型为false 直接在原始地址偏移量下拷贝false
		{
			out = ensure(p, 6);			// 检查当前偏移量下还能否放进去6个字符 "false\0" 并返回偏移后的地址
			if (out != NULL)
				strcpy(out, "false");
			break;
		}
		case cJSON_True:				// 输出缓冲区指针不为NULL且类型为true 直接在原始地址偏移量下拷贝true
		{
			out = ensure(p, 5);			// 检查当前偏移量下还能否放进去6个字符 "false\0" 并返回偏移后的地址
			if (out != NULL)
				strcpy(out, "true");
			break;
		}
		case cJSON_Number:				// 输出缓冲区指针不为NULL且类型为数字  
			out = print_number(item, p);// 将结构体的数字存入到输出缓冲区中 还会检测能否放下
			break;
		case cJSON_String:				// 输出缓冲区指针不为NULL且类型为数字
			out = print_string(item, p);// 将结构体的字符串存入到输出缓冲区中 还会检测能否放下
			break;
		case cJSON_Array:				// 输出缓冲区指针不为NULL且类型为数字
			out = print_array(item, depth, fmt, p);	// 将结构体的数组存入到输出缓冲区中 还会检测能否放下
			break;
		case cJSON_Object:				// 输出缓冲区指针不为NULL且类型为数字
			out = print_object(item, depth, fmt, p);// 将结构体的对象存入到输出缓冲区中 还会检测能否放下
			break;
		}
	}
	else	// 输出缓冲结构指针为NULL  需要申请空间 首次申请不需要检查大小问题
	{
		switch (item->type)
		{
		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, NULL);
			break;
		case cJSON_String:	
			out = print_string(item, NULL);
			break;
		case cJSON_Array:	
			out = print_array(item, depth, fmt, NULL);
			break;
		case cJSON_Object:
			out = print_object(item, depth, fmt, NULL);
			break;
		}
	}
	return out;

parse_array

/*
作	用:从输入文本构建数组。
参	数:item-结构体指针   value-字符串指针
返回值:解析后的文本地址
*/
static const char* parse_array(cJSON* item, const char* value)
{
	cJSON* child = NULL;	// 节点指针(结构体指针)

	if (*value != '[')		// 不是数组 匹配失败
	{
		ep = value;
		return 0;
	}

	item->type = cJSON_Array;// 设置类型为数组
	value = skip(value + 1);// 更新指针,跳过一些空格和控制字符

	if (*value == ']')		// 空数组
	{
		return value + 1;
	}

	// 如果不是空数组,就再申请一块空间  这块空间是item的孩子节点
	item->child = child = cJSON_New_Item();

	if (NULL == item->child)// 空间申请失败
	{
		return NULL;
	}

	// 先将指针跳过控制字符和空格,然后将接下来字符串指针找匹配的函数,
	// 然后将其放入孩纸结构体中,最后再将指针跳过控制字符和空格
	value = skip(parse_value(child, skip(value)));

	if (NULL == value)		// 如果字符串指针走完了  就结束
	{
		return NULL;
	}

	while (*value == ',')	// 如果碰到逗号
	{
		cJSON* new_item;	// 定义新指针

		new_item = cJSON_New_Item();// 申请新的空间
		if (NULL == new_item)// 空间申请失败
		{
			return NULL;
		}

		// 该节点为孩子节点的弟弟节点  下面是将俩兄弟节点连接起来
		child->next = new_item;
		new_item->prev = child;

		child = new_item;	// 更新节点指针

		// 先将指针跳过控制字符和空格,然后将接下来字符串指针找匹配的函数,
		// 然后将其放入孩纸结构体中,最后再将指针跳过控制字符和空格	
		value = skip(parse_value(child, skip(value + 1)));

		if (NULL == value)	// 走完了
		{
			return NULL;
		}
	}

	if (*value == ']')		// 数组结束标志
	{
		return value + 1;
	}

	
	ep = value;				// 特殊情况,为找打数组结束标志
	return NULL;
}

print_array

/*
功	能:将cjson数组结构体转换为字符串格式
参	数:item数组结构体指针 depth-深度  fmt-调整格式  p-输出缓冲结构体指针
返回值:
*/
static char* print_array(cJSON* item, int depth, int fmt, printbuffer* p)
{
	char** entries;// 保存一个数组的地址。该数组是指针数组。
	char* out = NULL;
	char* ptr = NULL;
	char* ret = NULL;
	int len = 5;
	cJSON* child = item->child;
	int numentries = 0;// 数组元素个数
	int i = 0;		// 循环计数器
	int fail = 0;	// 处理出错标志
	size_t tmplen = 0;

	/* 计算数组元素个数 */
	while (NULL != child)
	{
		numentries++;
		child = child->next;
	}

	/* 显现的处理元素个数为0时 */
	if (0 == numentries)
	{
		if (NULL != p)
			out = ensure(p, 3);
		else
			out = (char*)cJSON_malloc(3);

		if (NULL != out)
			strcpy(out, "[]");

		return out;
	}

	if (NULL != p)
	{
		/* 组成输出数组 */
		i = p->offset;
		ptr = ensure(p, 1);
		if (NULL == ptr)
			return NULL;

		*ptr = '[';	
		p->offset++;

		child = item->child;
		while (NULL != child && 0 == fail)
		{
			print_value(child, depth + 1, fmt, p);
			p->offset = update(p);
			if (NULL != child->next)
			{
				len = fmt ? 2 : 1;
				ptr = ensure(p, len + 1);
				if (NULL == ptr)
					return NULL;

				*ptr++ = ',';
				if (0 != fmt)
					*ptr++ = ' ';

				*ptr = '\0';
				p->offset += len;
			}
			child = child->next;
		}
		ptr = ensure(p, 2);
		if (!ptr)
			return 0;
		*ptr++ = ']';
		*ptr = 0;
		out = (p->buffer) + i;
	}
	else
	{
		/* 分配一个指针数组来保存每个元素的指针*/
		entries = (char**)cJSON_malloc(numentries * sizeof(char*));
		if (NULL == entries)// 内存申请失败
			return 0;
		memset(entries, 0, numentries * sizeof(char*));// 初始化内存

		/* 检索所有结果: */
		child = item->child;
		while (NULL != child && 0 == fail)
		{
			ret = print_value(child, depth + 1, fmt, 0);
			entries[i++] = ret;
			if (NULL != ret)
				len += strlen(ret) + 2 + (fmt ? 1 : 0);
			else
				fail = 1;// 结束标记
			child = child->next;// 指向它的兄弟节点
		}

		/* 如果没有失败,尝试malloc输出字符串 */
		if (0 == fail)
			out = (char*)cJSON_malloc(len);

		if (NULL == out)
			fail = 1;


		if (0 != fail)
		{
			for (i = 0; i < numentries; i++)
			{
				if (entries[i])
					cJSON_free(entries[i]);
			}
			cJSON_free(entries);
			return NULL;
		}


		*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 (0 != fmt)
					*ptr++ = ' ';
				*ptr = '\0';
			}
			cJSON_free(entries[i]);
		}
		cJSON_free(entries);
		*ptr++ = ']';
		*ptr++ = '\0';
	}
	return out;
}

parse_object

/*
功	能:从文本构建一个对象(就是{....}包含的)
参	数:item-结构体 value-字符串指针
返回值:字符串新的指针
*/
static const char* parse_object(cJSON* item, const char* value)
{
	cJSON* child = NULL;

	if ('{' != *value)			// 不是对象
	{
		ep = value;				// 将后面不能解析的字符串给到ep
		return NULL;
	}

	item->type = cJSON_Object;	// 设置类型
	value = skip(value + 1);	// 从{后面开始跳过控制字符和空格

	if ('}' == *value)			// 空对象
	{
		return value + 1;
	}


	child = cJSON_New_Item();	// 确定{}中间有数据,就申请空间
	if (NULL == child)			// 申请空间失败
	{
		return NULL;
	}
	item->child = child;		// 连接当前节点

	// 先将指针跳过控制字符和空格,然后将字符串放入堆区,
	// 最后再将指针跳过控制字符和空格
	value = skip(parse_string(child, skip(value)));

	if ('\0' == value)			// 指针走到头了
	{
		return NULL;
	}

	child->string = child->valuestring;	// 将key存入string中,
	child->valuestring = '\0';

	if (':' != *value)			// 没找到":" 
	{
		ep = value;
		return NULL;
	}

	// 先将指针跳过控制字符和空格,然后将接下来字符串指针找匹配的函数,
	// 然后将其放入孩纸结构体中,最后再将指针跳过控制字符和空格
	value = skip(parse_value(child, skip(value + 1)));
	if ('\0' == value)			// 指针走到头了
	{
		return NULL;
	}

	while (',' == *value)		// 还有,表示还有数据
	{
		cJSON* new_item;
		new_item = cJSON_New_Item();
		if (NULL == new_item)	// 申请空间失败
		{
			return NULL;
		}

		// 将兄弟节点连接起来
		child->next = new_item;
		new_item->prev = child;

		child = new_item;		// 更新节点指针

		// 先将指针跳过控制字符和空格,然后将接下来字符串拷贝到堆区
		// 最后再将指针跳过控制字符和空格
		value = skip(parse_string(child, skip(value + 1)));
		if ('\0' == value)		// 字符串指针遇到'\0'
		{
			return NULL;
		}

		
		child->string = child->valuestring;// 将key放入string中
		child->valuestring = '\0';

		if (':' != *value)		// 未找到":"
		{
			ep = value;
			return NULL;
		}

		// 先将指针跳过控制字符和空格,然后将接下来对字符串做相应处理
		// 最后再将指针跳过控制字符和空格
		value = skip(parse_value(child, skip(value + 1)));
		if (value == NULL)
			return NULL;
	}

	if ('}' == *value)			// {}结束标记
	{
		return value + 1;
	}
	
	ep = value;					// 匹配到'}' 发生错误
	return NULL;
}

print_object

/*
功	能:将对象结构体转换为字符串存储在输出缓冲区中
参	数:item-对象   depth-深度  fmt-输出格式(1-有换行符 0-无)    p-输出缓冲结构体指针
返回值:返回字符串在堆区起始地址
*/
static char* print_object(cJSON* item, int depth, int fmt, printbuffer* p)
{
	char** entries = NULL;				// 保存item中元素的value在输出缓冲区的地址
	char** names  = NULL;				// 保存item中元素的key在输出缓冲区的地址
	char* out = NULL;					// 保存item转换为文本在堆区的位置
	char* ptr = NULL;
	char* ret = NULL;
	char* str = NULL;
	int len = 7;						// 字符串的长度 最少 "","" 7个字节
	int i = 0, j = 0;					// 循环计数器
	cJSON* child = item->child;			// 指向item的第一个儿子
	int numentries = 0;					// 保存对象中对的个数
	int fail = 0;						// 保存到输出缓冲区的出错标志
	size_t tmplen = 0;
	
	while (NULL != child)				// 计算有多少对元素
	{
		numentries++;
		child = child->next;
	}
	
	if (0 == numentries)				// 键值对的个数为0
	{
		if (NULL != p)					// 输出缓冲结构体指针不为NULL
		{
			// 判断当前偏移量下比较len+3(因为需要放俩个""和一个\0)长度的字符是否越界并作出保护措施
			out = ensure(p, fmt ? depth + 4 : 3);
		}
		else
		{
			out = (char*)cJSON_malloc(fmt ? depth + 4 : 3);
		}

		if (NULL == out)				// 申请空间失败
		{
			return 0;
		}

		ptr = out;
		*ptr++ = '{';					// 大括号开

		if (0 != fmt)
		{
			*ptr++ = '\n';
			for (i = 0; i < depth - 1; i++)
				*ptr++ = '\t';
		}
		*ptr++ = '}';					// 大括号闭
		*ptr++ = '\0';
		return out;
	}
	if (NULL != p)						// 输出缓冲结构体指针不为NULL
	{
		/* 组成的输出: */
		i = p->offset;
		len = fmt ? 2 : 1;				// 有换行符多一个字符"\n\0"

		// 判断当前偏移量下比较len+1(有换行符三个字节,无换行符俩个字节)长度的字符是否越界并作出保护措施
		ptr = ensure(p, len + 1);
		if (NULL == ptr)				// 地址无效
			return NULL;

		*ptr++ = '{';					// 大括号开

		if (1 == fmt)					// 判断有无换行符
			*ptr++ = '\n';

		*ptr = '\0';
		p->offset += len;				// 输出缓冲结构体的偏移量更新
		child = item->child;			// 指向下一个儿子
		depth++;						// 深度+1
		while (NULL != child)
		{
			if (1 == fmt)				// 有换行符
			{
				ptr = ensure(p, depth);	// 安全检查
				if (NULL == ptr)		// 指针无效
					return NULL;

				for (j = 0; j < depth; j++)
					*ptr++ = '\t';

				p->offset += depth;		// 更新输出缓冲结构体指针的偏移量
			}
			print_string_ptr(child->string, p);// 将字符串转换成文本模式
			p->offset = update(p);		// 获取缓冲区的字符串长度

			len = fmt ? 2 : 1;			// 计算长度
			ptr = ensure(p, len);		// 安全检查
			if (NULL == ptr)			// 指针无效
				return NULL;

			*ptr++ = ':';

			if (1 == fmt)
				*ptr++ = '\t';

			p->offset += len;			// 更新输出缓冲结构体指针偏移量

			print_value(child, depth, fmt, p);// 将json结构体转换为字符串。
			p->offset = update(p);		// 获取缓冲区的字符串长度

			len = (fmt ? 1 : 0) + (child->next ? 1 : 0);

			ptr = ensure(p, len + 1);	// 安全检查
			if (NULL == ptr)			// 指针无效
				return NULL;

			if (NULL != child->next)	// 判断还有无元素(节点)
				*ptr++ = ',';

			if (1 == fmt) 
				*ptr++ = '\n';

			*ptr = '\0';
			p->offset += len;			// 更新输出缓冲结构体指针偏移量
			child = child->next;		// 继续下一个节点
		}
		ptr = ensure(p, fmt ? (depth + 1) : 2);	// 安全检查 考虑了换行符和tab
		if (NULL == ptr)				// 指针无效
			return 0;

		if (1 == fmt)
		{
			for (i = 0; i < depth - 1; i++)		// 深度
				*ptr++ = '\t';
		}
		*ptr++ = '}';
		*ptr = '\0';
		out = (p->buffer) + i;
	}
	else	// 输出缓冲结构体指针不为NULL
	{
		entries = (char**)cJSON_malloc(numentries * sizeof(char*));	// 给value申请空间
		if (NULL == entries)			// 申请空间失败
		{
			return NULL;
		}
		names = (char**)cJSON_malloc(numentries * sizeof(char*));	// 给key申请空间
		if (NULL == names)				// 申请空间失败
		{
			cJSON_free(entries);		// 释放前面申请成功的空间
			return NULL;
		}
		memset(entries, 0, sizeof(char*) * numentries);				// 给value空间初始化
		memset(names, 0, sizeof(char*) * numentries);				// 给key空间初始化

		/* 将所有结果收集到我们的数组中: */
		child = item->child;			// 指向数组第一个元素
		depth++;						// 深度增加 \t

		if (0 != fmt)					
			len += depth;

		while (NULL != child)
		{
			names[i] = str = print_string_ptr(child->string, 0);	// 返回该元素key字符串在输出缓冲区的起始位置
			entries[i++] = ret = print_value(child, depth, fmt, 0);	// 返回该元素value字符串在输出缓冲区的起始位置
			
			if (NULL != str && NULL != ret)
			{
				// fmt为换行符 depth为深度\t 深度可叠加 但是换行符最多有一个
				// key为一个字符串 value为一个字符串 所以会多出来俩个\0 因为strlen不算\0 
				len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0);
			}
			else						// 出错了
			{
				fail = 1;
			}
				
			child = child->next;		// 处理下一个元素(下一个节点)
		}

		/* 尝试分配输出字符串 */
		if (0 == fail)
			out = (char*)cJSON_malloc(len);
		if (NULL == out)				// 申请失败
			fail = 1;

		/* 处理失败 */
		if (1 == fail)
		{
			for (i = 0; i < numentries; i++)	// 将俩个数组中保存堆区开辟的空间依次释放掉
			{
				if (NULL != names[i])
					cJSON_free(names[i]);

				if (NULL != entries[i])
					cJSON_free(entries[i]);
			}
			cJSON_free(names);
			cJSON_free(entries);
			return NULL;
		}

		/* 组成的输出: */
		*out = '{';								// 存入大括开
		ptr = out + 1;							// 指向未填充的地址
		if (1 == fmt)							// 有换行符
			*ptr++ = '\n';

		*ptr = '\0';					
		for (i = 0; i < numentries; i++)
		{
			if (1 == fmt)
			{
				for (j = 0; j < depth; j++)		// 深度
					*ptr++ = '\t';
			}

			tmplen = strlen(names[i]);			// 计算当前key字符串的长度
			memcpy(ptr, names[i], tmplen);		// 拷贝
			ptr += tmplen;						// 指针更新
			*ptr++ = ':';					
			if (1 == fmt)
				*ptr++ = '\t';

			strcpy(ptr, entries[i]);			// 拷贝当前value字符串
			ptr += strlen(entries[i]);			// 指针更新
			if (i != numentries - 1)
				*ptr++ = ',';

			if (1 == fmt)
				*ptr++ = '\n';

			*ptr = '\0';
			cJSON_free(names[i]);				// 释放掉当前键值对所占的内存
			cJSON_free(entries[i]);
		}

		cJSON_free(names);
		cJSON_free(entries);

		if (1 == fmt)							// 格式操作
		{
			for (i = 0; i < depth - 1; i++) 
				*ptr++ = '\t';
		}
		*ptr++ = '}';							// 大括号闭
		*ptr++ = '\0';
	}
	return out;									// 返回字符串在堆区起始地址
}

cJSON_GetArraySize

/* 获取数组大小/对象个数 */
int cJSON_GetArraySize(cJSON* array)
{
	cJSON* c = array->child;	// 指向第一个儿子
	int i = 0;
	while (NULL != c)
	{
		i++;
		c = c->next;
	}
	return i;				
}

cJSON_GetArrayItem

/* 查找数组结构体第item(从0开始)个子结构体的地址并返回 */
cJSON* cJSON_GetArrayItem(cJSON* array, int item)
{
	cJSON* c = array->child;
	while (NULL != c && item > 0)
	{
		item--;
		c = c->next;
	}
	return c;
}

cJSON_GetObjectItem

/* 查找对象结构体中key为string的子结构体的地址并返回 */
cJSON* cJSON_GetObjectItem(cJSON* object, const char* string)
{
	cJSON* c = object->child;
	while (NULL != c && 0 != cJSON_strcasecmp(c->string, string))
	{
		c = c->next;
	}
	return c;
}

suffix_object

/* 将兄弟节点连接起来  类似与链表结构 */
static void suffix_object(cJSON* prev, cJSON* item)
{
	prev->next = item;
	item->prev = prev;
}

create_reference

/* 用于处理引用的实用程序(拷贝一份item结构体)  */
static cJSON* create_reference(cJSON* item)
{
	cJSON* ref = cJSON_New_Item();
	if (NULL == ref)// 申请失败
	{
		return NULL;
	}

	memcpy(ref, item, sizeof(cJSON));// 内存初始化
	ref->string = '\0';
	ref->type |= cJSON_IsReference;
	ref->next = NULL;
	ref->prev = NULL;

	return ref;
}

cJSON_AddItemToArray


/*
功	能:添加元素(子结构体)到数组(父结构体)最后
参	数:array-数组结构体  item-元素子结构体
返回值:无
*/
void cJSON_AddItemToArray(cJSON* array, cJSON* item)
{
	cJSON* c = array->child;		// 指向父结构体的第一个儿子

	if (NULL == item)				// 若发现子结构体指针为空就直接结束
	{
		return;
	}

	if (NULL == c)					// 若发现父结构的儿子为空时,直接添加
	{
		array->child = item;
	}
	else							// 若发现父结构的儿子不为空时,就开始找最后那个儿子节点
	{
		while (NULL != c && NULL != c->next)
		{
			c = c->next;
		}
		suffix_object(c, item);		// 连接新节点函数
	}
}

cJSON_AddItemToObject

/*
功	能:添加子结构到对象中
参	数:object-对象  string-新子结构的key  item-新结构体的value
返回值:无
*/
void cJSON_AddItemToObject(cJSON* object, const char* string, cJSON* item)
{
	if (NULL == item)						// 子结构体指针为NULL
	{
		return;
	}

	if ('\0' != item->string)				// 子结构体中的key(string)不为空.就释放掉
	{
		cJSON_free(item->string);
	}

	// 先在堆区开辟空间将key(string)放入然后再将堆区地址给到key(string)保管
	item->string = cJSON_strdup(string);

	cJSON_AddItemToArray(object, item);		// 将子结构体放入对象(jbject)中
}

cJSON_AddItemToObjectCS

/*
功	能:添加元素结构体到对象结构体中
参	数:object-对象  string-新添加元素的key   item-新添加元素的value
返回值:无
*/
void cJSON_AddItemToObjectCS(cJSON* object, const char* string, cJSON* item)
{
	if (NULL == item)
	{
		return;
	}

	if (0 == (item->type & cJSON_StringIsConst) && '\0' != item->string)  // 若item的key存在就先释放
	{
		cJSON_free(item->string);
	}

	item->string = (char*)string;		// 将key放入item
	item->type |= cJSON_StringIsConst;	// 将type放入item
	cJSON_AddItemToArray(object, item); // 再将item放入对象(数组)中
}

cJSON_AddItemReferenceToArray

/* 添加元素引用到数组中 */
void cJSON_AddItemReferenceToArray(cJSON* array, cJSON* item)
{
	cJSON_AddItemToArray(array, create_reference(item));
}

cJSON_AddItemReferenceToObject

/* 添加元素引用到对象中 */
void cJSON_AddItemReferenceToObject(cJSON* object, const char* string, cJSON* item)
{
	cJSON_AddItemToObject(object, string, create_reference(item));
}

cJSON_DetachItemFromArray

/*
功	能:将元素从数组中分离
参	数:array-数组  which-下标(要分离元素的下标)
返回值:分离后元素的地址
*/
cJSON* cJSON_DetachItemFromArray(cJSON* array, int which)
{
	cJSON* c = array->child;		// 指向第一个儿子(第0个元素)

	while (NULL != c && which > 0)	// 寻找要分离的元素位置
	{
		c = c->next;
		which--;
	}

	if (NULL == c)					// 指针为NULL
	{
		return NULL;
	}

	if (NULL != c->prev)			// 要分离的元素不是第0个元素
	{
		c->prev->next = c->next;
	}

	if (NULL != c->next)			// 要分离的元素不是最后一个元素
	{
		c->next->prev = c->prev;
	}

	if (c == array->child)			// 要分离的元素是第0个元素
	{
		array->child = c->next;
	}

	
	c->prev = c->next = NULL;		// 其他情况
	return c;
}

cJSON_DeleteItemFromArray

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

cJSON_DetachItemFromObject

/*
功	能:从对象中分离元素
参	数:object-对象  string-要查找元素的key
返回值:元素分离后的地址
*/
cJSON* cJSON_DetachItemFromObject(cJSON* object, const char* string)
{
	int i = 0;
	cJSON* c = object->child;
	while (NULL != c && 0 != cJSON_strcasecmp(c->string, string)) // 查找要删除的key为string
	{
		i++;
		c = c->next;
	}

	if (NULL != c)													// 找到了
		return cJSON_DetachItemFromArray(object, i);
	return NULL;
}

cJSON_DeleteItemFromObject

/* 从对象中删除元素 */
void   cJSON_DeleteItemFromObject(cJSON* object, const char* string)
{
	cJSON_Delete(cJSON_DetachItemFromObject(object, string));
}

JSON_InsertItemInArray

/*
功	能:添加元素到数组或者对象中
参	数:array-数组  which-插入的下标(从0开始)  newitem-插入的新元素(新节点)
返回值:无
*/
void JSON_InsertItemInArray(cJSON* array, int which, cJSON* newitem)
{
	cJSON* c = array->child;					// 用于保存插入的元素节点
	while (NULL != c && which > 0)				// 找插入的位置
	{
		c = c->next;
		which--;
	}

	if (NULL == c)								// 发现插入位置大于等于数组元素个数
	{
		cJSON_AddItemToArray(array, newitem);	// 添加到数组最后
		return;
	}

	/* 连接新节点 */
	newitem->next = c;							
	newitem->prev = c->prev;
	c->prev = newitem;

	if (c == array->child)						// 如果是第0个元素(第一个儿子)
		array->child = newitem;
	else
		newitem->prev->next = newitem;
}

cJSON_ReplaceItemInArray

/*
作	用:将newiem节点替换为array的第which子节点
参	数:array-根节点, which-替换的第几个子节点  newitem-新的子节点
返回值:无
*/
void cJSON_ReplaceItemInArray(cJSON* array, int which, cJSON* newitem)
{
	cJSON* c = array->child;

	while (NULL != c && which > 0)			// 找到需要替换的子节点
	{
		c = c->next;
		which--;
	}

	if (NULL == c)							// 替换的位置超过目前节点
	{
		return;
	}

	newitem->next = c->next;				// 新节点连接弟弟节点
	newitem->prev = c->prev;				// 新节点连接哥哥节点

	if (NULL != newitem->next)				// 新节点不是最后一个		
	{
		newitem->next->prev = newitem;		// 弟弟节点连接新节点
	}


	if (c == array->child)					// 若被替换的节点是父节点的第一个孩子节点
	{
		array->child = newitem;				// 就将父节点的孩子节点设置为新节点
	}
	else
	{
		newitem->prev->next = newitem;		// 哥哥节点连接新节点
	}


	c->next = NULL;							// 将旧节点指针置为空
	c->prev = NULL;							// 将旧节点指针置为空
	cJSON_Delete(c);						// 回收被替换的指针内存
}

cJSON_ReplaceItemInObject

/*
作	用:用新的value,替换object对象中key==string那一个子节点
参	数:object-被替换的根对象的一个子节点    string-被替换的key    newitem-新子节点的value
返回值:无
*/
void cJSON_ReplaceItemInObject(cJSON* object, const char* string, cJSON* newitem)
{
	int i = 0;
	cJSON* c = object->child;

	while (NULL != c && cJSON_strcasecmp(c->string, string))// 查找object下key为string的子节点
	{
		i++;
		c = c->next;
	}

	if (NULL != c)											// 找到了
	{
		newitem->string = cJSON_strdup(string);				// 在堆区存放字符串的地址给到子节点的key
		cJSON_ReplaceItemInArray(object, i, newitem);		// 将object的第i个子节点替换为newitem
	}
}

cJSON_CreateNull

/* 创建子结构体(类型为NULL) */
cJSON* cJSON_CreateNull(void)
{
	cJSON* item = cJSON_New_Item();					// 向堆区给结构体申请一块空间
	if (NULL == item)								// 申请空间成功
	{
		return NULL;
	}
	item->type = cJSON_NULL;						// 将结构体的type类型设置为true
	return item;									// 返回结构体地址
}

cJSON_CreateTrue

/* 创建子结构体(类型为true) */
cJSON* cJSON_CreateTrue(void)
{
	cJSON* item = cJSON_New_Item();					// 向堆区给结构体申请一块空间
	if (NULL == item)								// 申请空间成功
	{
		return NULL;
	}
	item->type = cJSON_True;						// 将结构体的type类型设置为true
	return item;									// 返回结构体地址
}

cJSON_CreateFalse

/* 创建子结构体(类型为false) */
cJSON* cJSON_CreateFalse(void)
{
	cJSON* item = cJSON_New_Item();					// 向堆区给结构体申请一块空间
	if (NULL == item)								// 申请空间失败
	{
		return NULL;
	}
	item->type = cJSON_False;						// 将结构体的type类型设置为false
	return item;									// 返回结构体地址
}

cJSON_CreateBool

/* 创建子结构体(类型为bool) */
cJSON* cJSON_CreateBool(int b)
{
	cJSON* item = cJSON_New_Item();					// 向堆区给结构体申请一块空间
	if (NULL == item)								// 申请空间失败
	{	
		return NULL;
	}
	item->type = b ? cJSON_True : cJSON_False;		// 设置结构体的type类型为bool
	return item;						
}

cJSON_CreateNumber

/* 创建子结构体(类型为数字) */
cJSON* cJSON_CreateNumber(double num)
{
	cJSON* item = cJSON_New_Item();					// 向堆区给结构体申请一块空间
	if (NULL == item)								// 申请空间失败
	{
		return NULL;
	}
	item->type = cJSON_Number;						// 将结构体的type类型设置为数字
	item->valuedouble = num;						// 将num给到valuedouble
	item->valueint = (int)num;						// 将num给到valueint
	return item;									
}

cJSON_CreateString


/* 创建子结构体(类型为字符串) */
cJSON* cJSON_CreateString(const char* string)		// 参数const防止字符串被修改
{
	cJSON* item = cJSON_New_Item();					// 向堆区给结构体申请一块空间
	if (NULL == item)								// 申请空间失败
	{
		return NULL;
	}
	item->type = cJSON_String;						// 将结构体的type类型设置为字符串
	item->valuestring = cJSON_strdup(string);		// 将堆区开辟的空间地址给到valuestring中
	return item;								
}

cJSON_CreateArray


/* 创建子结构体(类型为数组)  */
cJSON* cJSON_CreateArray(void)
{
	cJSON* item = cJSON_New_Item();					// 向堆区给结构体申请一块空间
	if (NULL == item)								// 申请空间失败
	{
		return NULL;
	}
	item->type = cJSON_Array;						// 将结构体的type类型设置为字符串
	return item;
}

cJSON_CreateObject

/* 创建子结构体(类型为对象)  */
cJSON* cJSON_CreateObject(void)
{
	cJSON* item = cJSON_New_Item();					// 向堆区给结构体申请一块空间
	if (NULL == item)								// 申请空间失败
	{
		return NULL;
	}
	item->type = cJSON_Object;						// 将结构体的type类型设置为字符串
	return item;
}

cJSON_CreateIntArray

/*
功	能:创建一个数组结构体
参	数:number-数组指针	count-数组的长度
返回值:返回数组结构体地址
*/
cJSON* cJSON_CreateIntArray(const int* numbers, int count)
{
	int i = 0;								
	cJSON* n = 0;							// 保存创建的子结构体(子节点)地址
	cJSON* p = 0;							// 保存每次更新后的前一个节点(父节点或者哥哥节点)
	cJSON* a = cJSON_CreateArray();			// 保存创建的父结构体(父节点)地址
	if (NULL == a)							// 堆区内存申请失败
		return NULL;

	for (i = 0; i < count; i++)
	{
		n = cJSON_CreateNumber(numbers[i]);	// 创建子结构体(类型为数字)
		if (NULL == n)						// 堆区内存申请失败
			return NULL;
		if (0 == i)							// 第一个子节点,就是父节点的儿子(child)
			a->child = n;
		else								
			suffix_object(p, n);			// 将兄弟节点连接起来
		p = n;								// 更新前一个节点
	}
	return a;								// 返回父节点的地址
}

cJSON_CreateFloatArray

/*
功	能:创建浮点型数组
参	数:numbers-双精度数组指针 count-数组个数
返回值:返回数组结构体指针
*/
cJSON* cJSON_CreateFloatArray(const float* numbers, int count)
{
	int i = 0;
	cJSON* n = 0;							// 接收数字类型结构体的地址
	cJSON* p = 0;							// 保存每次更新后的前一个节点(父节点或者哥哥节点)
	cJSON* a = cJSON_CreateArray();			// 保存创建的父结构体(父节点)地址
	if (NULL == a)							// 堆区内存申请失败
		return NULL;

	for (i = 0; NULL != a && i < count; i++)
	{
		n = cJSON_CreateNumber(numbers[i]);	// 创建子结构体(类型为数字)
		if (NULL == n)						// 堆区内存申请失败
			return NULL;
		if (0 == i)							// 第一个儿子
			a->child = n;
		else
			suffix_object(p, n);			// 连接兄弟节点
		p = n;								// 更新前一个节点
	}
	return a;
}

cJSON_CreateDoubleArray

/*
功	能:创建双进度型数组或者对象的结构体
参	数:numbers-双精度数组指针 count-数组个数
返回值:返回数组结构体指针
*/
cJSON* cJSON_CreateDoubleArray(const double* numbers, int count)
{
	int i = 0;
	cJSON* n = 0;								// 接收数字类型结构体的地址
	cJSON* p = 0;								// 保存每次更新后的前一个节点(父节点或者哥哥节点)
	cJSON* a = cJSON_CreateArray();				// 保存创建的父结构体(父节点)地址
	if (NULL == a)								// 堆区内存申请失败
		return NULL;

	for (i = 0; NULL != a && i < count; i++)
	{
		n = cJSON_CreateNumber(numbers[i]);		// 创建子结构体(类型为数字)
		if (NULL == n)							// 堆区内存申请失败
			return NULL;
		if (0 == i)								// 第一个儿子
			a->child = n;
		else
			suffix_object(p, n);				// 连接兄弟节点
		p = n;
	}
	return a;
}

cJSON_CreateStringArray

/*
功	能:创建字符串数组或者对象
参	数:strings-字符串数组指针 count-数组个数
返回值:返回数组结构体指针
*/
cJSON* cJSON_CreateStringArray(const char** strings, int count)
{
	int i = 0;
	cJSON* n = 0;								// 接收字符串类型结构体的地址
	cJSON* p = 0;								// 保存每次更新后的前一个节点(父节点或者哥哥节点)
	cJSON* a = cJSON_CreateArray();				// 保存创建的父结构体(父节点)地址
	if (NULL == a)								// 堆区内存申请失败
		return NULL;

	for (i = 0; NULL != a && i < count; i++)
	{
		n = cJSON_CreateString(strings[i]);		// 创建子结构体(类型为字符串)
		if (NULL == n)							// 堆区内存申请失败
			return NULL;
		if (0 == i)								// 第一个儿子
			a->child = n;
		else
			suffix_object(p, n);				// 连接兄弟节点
		p = n;
	}
	return a;
}

cJSON_Duplicate

/*
功	能:深复制一份结构体
参	数:item-结构体指针  recurse-递归层数
返回值:返回新复制的地址
*/
cJSON* cJSON_Duplicate(cJSON* item, int recurse)
{
	cJSON* newitem = NULL;		// 保存首地址
	cJSON* cptr = NULL;			// 拷贝区域的指针
	cJSON* nptr = NULL;			// 原区域的指针
	cJSON* newchild = NULL;		// 新子节点

	if (NULL == item)
		return NULL;

	newitem = cJSON_New_Item();
	if (NULL == newitem)
		return NULL;

	/* 复制所有的变量 */
	newitem->type = item->type;
	newitem->valueint = item->valueint;
	newitem->valuedouble = item->valuedouble;
	if (NULL != item->valuestring)									// 拷贝value
	{
		newitem->valuestring = cJSON_strdup(item->valuestring);
		if (NULL == newitem->valuestring)							// 开辟失败
		{
			cJSON_Delete(newitem);
			return NULL;
		}
	}
	if (NULL != item->string)										// 拷贝key
	{
		newitem->string = cJSON_strdup(item->string);
		if (NULL == newitem->string)								// 开辟失败
		{
			cJSON_Delete(newitem);
			return 0;
		}
	}

	/* 如果是非递归的,那么我们就完成了! */
	if (0 == recurse)									
		return newitem;

	/* 递归复制 */
	cptr = item->child;
	while (cptr)
	{
		newchild = cJSON_Duplicate(cptr, 1);
		if (NULL == newchild)								// 开辟失败
		{
			cJSON_Delete(newitem);
			return 0;
		}
		if (NULL != nptr)									// 不是第一个儿子									
		{
			nptr->next = newchild;							// 连接兄弟节点					
			newchild->prev = nptr;
			nptr = newchild;								// 更新拷贝区域的指针					
		}
		else												// 第一个儿子
		{
			newitem->child = newchild;						// 设置为父节点的儿子
			nptr = newchild;								// 更新拷贝区域的指针
		}			
		cptr = cptr->next;									// 更新原区域
	}
	return newitem;
}

cJSON_Minify

/*
功	能:移除文档中的所有空白和注释,实现对 JSON 内容的最小化压缩。实现无开销和近乎完美的表现	
参	数:json-结构体指针
返回值:无
*/
void cJSON_Minify(char* json)
{
	char* into = json;
	while ('\0' != *json)
	{
		if (*json == ' ')								// 是空格
			json++;
		else if (*json == '\t')							// 是制表符\t
			json++;
		else if (*json == '\r')							// \r回到当前行的行首,而不会换到下一行,如果接着输出的话,本行以前的内容会被逐一覆盖;
			json++;
		else if (*json == '\n')							// 换行符
			json++;
		else if (*json == '/' && json[1] == '/')		// C++风格注释
			while (*json && *json != '\n')				// 不是\0也不是换行符
				json++;
		else if (*json == '/' && json[1] == '*')		// c风格注释/*
		{
			while ('\0' != *json && !(*json == '*' && json[1] == '/'))	// 到*/注释结束
				json++;
			json += 2;									// 最后跳过 */ 俩个字节
		}
		else if (*json == '\"')							// 字符串 开始
		{
			*into++ = *json++;							// 开始拷贝
			while ('\0' != *json && *json != '\"')		// 到 \" 结束
			{
				if (*json == '\\')						// 发现是转义字符,会多占一个字节
					*into++ = *json++;
				*into++ = *json++;
			}
			*into++ = *json++;
		}
		else 
			*into++ = *json++;							// 其他所有字符
	}
	*into = '\0';
}

printbuffer(输出缓冲区结构)

/* 输出缓冲区结构体 */
typedef struct printbuffer
{
	char* buffer;		// 缓冲区
	int length;			// 长度
	int offset;			// 偏移量
} printbuffer;

自定义内存管理函数

static void* (*cJSON_malloc)(size_t sz) = malloc;		// 将cJSON_malloc定义为malloc函数指针
static void (*cJSON_free)(void* ptr) = free;			// 将cJSON_free定义为free函数指针

测试文件

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"

/*
作	用:将一个字符串文本解析为JSON格式,成功就返回文本在堆区的地址,失败就返回不能解析的地址然后打
	    印不能解析的字符串部分回收堆区结构体内存。然后打印输出。再回收堆区存储解析好的文本
参	数:字符串地址
返回值:无
 */
void doit(char* text)
{
	char* out;					// 用于保存解析后的字符串地址
	cJSON* json;				// 用于保存构造的树结构体地址

	json = cJSON_Parse(text);	// 使用json格式将字符串构造成一个树结构体,并保存在堆区,返回堆区地址
	if (json == NULL)
	{
		printf("Error before: [%s]\n", cJSON_GetErrorPtr());	// 打印不能被json解析的字符串
	}
	else
	{
		out = cJSON_Print(json);// 将树结构体使用json解析为字符串,并保存在堆区,返回堆区地址
		cJSON_Delete(json);		// 释放树结构体在堆区的空间
		printf("%s\n", out);	// 打印解析后的字符串
		free(out);				// 释放堆区解析好的字符串空间
	}
}

/* 从文件中读取字符串文本 */
void dofile(char* filename)
{
	FILE* f; // 文件指针
	long int len;
	char* data;

	f = fopen(filename, "rb");	// 以二进制方式读模式打开文件filename
	if (NULL == f)
	{
		printf("文件打开失败!\n");
		return;
	}
	fseek(f, 0, SEEK_END);		// 将文件指针定义到文件尾部,回退0个字节
	len = ftell(f);				// 返回文件指针与文件开始处的字节数,即文件总字节数
	fseek(f, 0, SEEK_SET);		// 将文件指针定义到文件开始处,向后0个字节
	data = (char*)calloc(len + 1, 1);		// 申请一块大于文件总字节数的空间,并初始化为0
	fread(data, 1, len, f);		// 将len个1大小字节的值拷贝到data内存中。返回成功读取的个数
	fclose(f);					// 关闭fp指定文件,必要时刷新缓冲区(关闭失败返回EOF,成功返回0)
	doit(data);					// 将字符串(data)先构造成树结构体,然后将书结构体使用json解析成字符串,并打印输出。
	free(data);					// 释放堆区解析好的字符串空间
}

/* 下面测试代码用到的结构体 */
struct record
{
	const char* precision;
	double lat, lon;
	const char* address, * city, * state, * zip, * country;
};

/* 创建一组对象作为演示 */
void create_objects(void)
{
	cJSON* root;				// 根节点(根结构体)
	cJSON* fmt;					// 用于保存视频实例中子对象的地址
	cJSON* img;					// 用于保存走廊实例中子对象的地址
	cJSON* thm;					// 用于保存走廊实例中子子对象的地址
	cJSON* fld;					// 用于保存唱片实例中子子对象的地址
	char* out;					// 保存解析后字符串在堆区的地址
	int i;						// 用于遍历

	/* 星期数(数组) */
	const char* strings[7] = { "Sunday","Monday","Tuesday","Wednesday",
		"Thursday","Friday","Saturday" };

	/* 矩阵(二维数组) */
	int numbers[3][3] = { {0,-1,0},{1,0,0},{0,0,1} };

	/* 走廊(数组) */
	int ids[4] = { 116,943,234,38793 };

	/* 唱片(结构体数组) */
	struct record fields[2] = 
	{
		{"zip",37.7668,-1.223959e+2,"","SAN FRANCISCO","CA","94107","US"},
		{"zip",37.371991,-1.22026e+2,"","SUNNYVALE","CA","94085","US"}
	};

	/* 构造视频实例*/

	// 创建一个结构体对象 将type类型设置为对象, 并申请一块地址空间。返回地址
	root = cJSON_CreateObject(); 

	// 创建子结构体value为"Jack (\"Bee\") Nimble",key为"name"。然后连接fmt父结构体
	cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));

	// 创建子结构体对象并保存子结构体地址到fmt中,设置key为"type"。然后连接fmt父结构体
	cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject());

	// 创建子结构体value为"rect",key为"type"。然后连接fmt父结构体
	cJSON_AddStringToObject(fmt, "type", "rect");

	// 创建子结构体value为1920,key为"width"。然后连接fmt父结构体
	cJSON_AddNumberToObject(fmt, "width", 1920);

	// 创建子结构体value为1080,key为"height"。然后连接fmt父结构体
	cJSON_AddNumberToObject(fmt, "height", 1080);

	// 创建子结构体value为false,key为"interlace"。然后连接fmt父结构体
	cJSON_AddFalseToObject(fmt, "interlace");

	// 创建子结构体value为24,key为"frame rate"。然后连接fmt父结构体
	cJSON_AddNumberToObject(fmt, "frame rate", 24);

	/* 树结构体解析成json格式字符串,并输出,回收堆区地址*/
	out = cJSON_Print(root);// 将root树结构解析成cjson格式字符串,并存放在堆区,成功返回堆区地址。失败返回NULL
	cJSON_Delete(root);		// 释放root树结构体在堆区的空间
	printf("%s\n", out);	// 打印解析后的字符串
	free(out);				// 释放堆区解析好的字符串空间

	/* 星期数(数组): */
	root = cJSON_CreateStringArray(strings, 7);// 构造星期数树结构(字符串数组)

	/* 树结构体解析成json格式字符串,并输出,回收堆区地址*/
	out = cJSON_Print(root);// 将root树结构解析成cjson格式字符串,并存放在堆区,成功返回堆区地址。失败返回NULL
	cJSON_Delete(root);		// 释放root树结构体在堆区的空间
	printf("%s\n", out);	// 打印解析后的字符串
	free(out);				// 释放堆区解析好的字符串空间

	/* 矩阵 */
	root = cJSON_CreateArray();// 构造矩阵树结构(整型数组)
	for (i = 0; i < 3; i++)
	{
		// 创建一个子结构体数组,并与root连接起来
		cJSON_AddItemToArray(root, cJSON_CreateIntArray(numbers[i], 3));
	}

	// 创建子结构体,并成为root的第1个孩子节点
	// cJSON_ReplaceItemInArray(root,1,cJSON_CreateString("Replacement"));  

	/* 树结构体解析成json格式字符串,并输出,回收堆区地址*/
	out = cJSON_Print(root);// 将root树结构解析成cjson格式字符串,并存放在堆区,成功返回堆区地址。失败返回NULL
	cJSON_Delete(root);		// 释放root树结构体在堆区的空间
	printf("%s\n", out);	// 打印解析后的字符串
	free(out);				// 释放堆区解析好的字符串空间


	/* 走廊 */
	root = cJSON_CreateObject();													// 创建子结构体(类型为对象) 
	cJSON_AddItemToObject(root, "Image", img = cJSON_CreateObject());				// 添加img子对象到root中
	cJSON_AddNumberToObject(img, "Width", 800);										// 添加子节点到img对象中
	cJSON_AddNumberToObject(img, "Height", 600);									// 添加子节点到img对象中
	cJSON_AddStringToObject(img, "Title", "View from 15th Floor");					// 添加子节点到img对象中
	cJSON_AddItemToObject(img, "Thumbnail", thm = cJSON_CreateObject());			// 添加thm子对象到img对象中
	cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943");	// 添加子节点到thm对象中
	cJSON_AddNumberToObject(thm, "Height", 125);									// 添加子节点到thm对象中
	cJSON_AddStringToObject(thm, "Width", "100");									// 添加子节点到thm对象中
	cJSON_AddItemToObject(img, "IDs", cJSON_CreateIntArray(ids, 4));				// 添加ids子数组到img对象中

	/* 树结构体解析成json格式字符串,并输出,回收堆区地址*/
	out = cJSON_Print(root);// 将root树结构解析成cjson格式字符串,并存放在堆区,成功返回堆区地址。失败返回NULL
	cJSON_Delete(root);		// 释放root树结构体在堆区的空间
	printf("%s\n", out);	// 打印解析后的字符串
	free(out);				// 释放堆区解析好的字符串空间

	/* 唱片 */
	root = cJSON_CreateArray();											// 创建子结构体(类型为数组) 
	for (i = 0; i < 2; i++)												// 二维数组
	{
		cJSON_AddItemToArray(root, fld = cJSON_CreateObject());			// 添加fld子对象到root中
		cJSON_AddStringToObject(fld, "precision", fields[i].precision);	// 添加子节点到fld对象中
		cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat);		// 添加子节点到fld对象中
		cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon);		// 添加子节点到fld对象中
		cJSON_AddStringToObject(fld, "Address", fields[i].address);		// 添加子节点到fld对象中
		cJSON_AddStringToObject(fld, "City", fields[i].city);			// 添加子节点到fld对象中
		cJSON_AddStringToObject(fld, "State", fields[i].state);			// 添加子节点到fld对象中
		cJSON_AddStringToObject(fld, "Zip", fields[i].zip);				// 添加子节点到fld对象中
		cJSON_AddStringToObject(fld, "Country", fields[i].country);		// 添加子节点到fld对象中
	}

	// 创建子数组结构体,并成为root的第1个子对象且key==city的value
	// cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root,1),"City",cJSON_CreateIntArray(ids,4));

	/* 树结构体解析成json格式字符串,并输出,回收堆区地址*/
	out = cJSON_Print(root);// 将root树结构解析成cjson格式字符串,并存放在堆区,成功返回堆区地址。失败返回NULL
	cJSON_Delete(root);		// 释放root树结构体在堆区的空间
	printf("%s\n", out);	// 打印解析后的字符串
	free(out);				// 释放堆区解析好的字符串空间
}

#if 1
int main(int argc, const char* argv[]) 
{
	/* 一堆 cjson */
	char text1[] = "{\n\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n\"format\": {\"type\":       \"rect\", \n\"width\":      1920, \n\"height\":     1080, \n\"interlace\":  false,\"frame rate\": 24\n}\n}";
	char text2[] = "[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]";
	char text3[] = "[\n    [0, -1, 0],\n    [1, 0, 0],\n    [0, 0, 1]\n	]\n";
	char text4[] = "{\n		\"Image\": {\n			\"Width\":  800,\n			\"Height\": 600,\n			\"Title\":  \"View from 15th Floor\",\n			\"Thumbnail\": {\n				\"Url\":    \"http:/*www.example.com/image/481989943\",\n				\"Height\": 125,\n				\"Width\":  \"100\"\n			},\n			\"IDs\": [116, 943, 234, 38793]\n		}\n	}";
	char text5[] = "[\n	 {\n	 \"precision\": \"zip\",\n	 \"Latitude\": 37.7668,\n	 \"Longitude\": -122.3959,\n	 \"Address\":   \"\",\n	 \"City\":      \"SAN FRANCISCO\",\n	 \"State\":     \"CA\",\n	 \"Zip\":       \"94107\",\n	 \"Country\":   \"US\"\n	 },\n	 {\n	 \"precision\": \"zip\",\n	 \"Latitude\":  37.371991,\n	 \"Longitude\": -122.026020,\n	 \"Address\":   \"\",\n	 \"City\":      \"SUNNYVALE\",\n	 \"State\":     \"CA\",\n	 \"Zip\":       \"94085\",\n	 \"Country\":   \"US\"\n	 }\n	 ]";
	

	/* 字符串方式调用 */
	doit(text1);
	doit(text2);
	doit(text3);
	doit(text4);
	doit(text5);

	/* 从文件中读取字符串文本 */
/*	dofile("../../tests/test2"); */
/*	dofile("../../tests/test3"); */
/*	dofile("../../tests/test4"); */
/*	dofile("../../tests/test5"); */

	/* Now some samplecode for building objects concisely: */
	 create_objects();

	return 0;
}
#endif

由于笔者水平有限,可能会有一些错误欢迎大佬帮忙勘正。

  • 12
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林夕07

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值