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.c
和cJSON.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; }