关于cjson的介绍和使用方法就不在这里介绍了,详情请查看上一篇博客cjson使用方法

JSON的内存结构像广义表,可以认为是有层次的双向链表。

cJSON程序中的细节点如下:

  • 大量宏替换
  • 大量静态函数
  • 错误处理机制
  • 字符串处理时存在utf16转utf9,编码转换
  • 用函数指针封装malloc,free,方便用于处理,比如在申请后初始化,或者释放前进行一些处理等。

cJSON中的重要接口函数如下:

解析函数
cJSON * cJSON_Parse(const char *value);
打印函数
char * cJSON_Print(cJSON * item);
删除函数
void cJSON_Delete(cJSON * c);
构造函数
create系列和add系列
解析字符串
char *parse_string(cJSON*item,const char *str)
解析数字
char *parse_number(cJSON *item,const char *num)
解析数组
char *parse_array(cJSON *item,const char *value)
解析对象
char *parse_object(cJSON *item,const char *value)
......

cjosn有两个相关的文件,一个cJSON.c和cJSON.h。我们先从头文件开始分析。

首先,我们会看到头文件的开头和结尾这样的语句:

  1. #ifndef cJSON__h
  2. #define cJSON__h
  3. #ifdef __cplusplus
  4. extern "C"
  5. {
  6. #endif

...

...

  1. #ifdef __cplusplus
  2. }
  3. #endif
  4. #endif

#ifndef cJSON_h,#define cJSON_h,#endif . 这是为了防止头文件被重复引用。

extern
"C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern
"C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。

接着往下看,就会看到cjson的结构体的定义,cjson对象存储结构实际是一个结构体。

  1. // JSON的一个value的结构体
  2. typedef struct cJSON
  3. {
  4. struct cJSON *next,*prev;    // 同一级的元素使用双向列表存储
  5. struct cJSON *child;           // 如果是一个object或array的话,child为第一个儿子的指针
  6. int type;                            // value的类型
  7. char *valuestring;             // 如果这个value是字符串类型,则此处为字符串值
  8. int valueint;                      // 如果是数字的话,整数值
  9. double valuedouble;            // 如果是数字的话,读点数值
  10. char *string;                      // json对象的名称
  11. } cJSON;

前面提到过,cjson的存储结构像一个广义表,其实也可以说是一个树,不过兄弟结点之间都通过prev和next两个指针连接起来。

prev和next分别是cjson对象的前驱和后继,属于同一级别的对象。chid则指向孩子结点,并且是第一个孩子的指针。

示例图如下:

cjson的类型宏定义:

  1. /* cJSON Types: */
  2. #define cJSON_False  (1 << 0)
  3. #define cJSON_True   (1 << 1)
  4. #define cJSON_NULL   (1 << 2)
  5. #define cJSON_Number (1 << 3)
  6. #define cJSON_String (1 << 4)
  7. #define cJSON_Array  (1 << 5)
  8. #define cJSON_Object (1 << 6)
  9. #define cJSON_IsReference 256
  10. #define cJSON_StringIsConst 512

这些宏定义是对结构体type的值定义,处理时只需要将type的值&255进行位运算,即可得到json里储存的数据类型。

cjson的创建:

cjson的创建的过程就是创建一个cjson结构体,再通过add一系列函数将其他孩子结点数据或同等级结点加入,将相关结点通过指针链起来。

cjson_create一系列函数:cJSON_CreateArray(),cJSON_CreateObject(),cJSON_CreateString()等函数,都是调用cJSON_New_Item()函数创建对应节点信息。函数返回一个json结构体指针。

相关函数如下:

  1. static cJSON *cJSON_New_Item(void)   //创建json结构体
  2. {
  3. cJSON *node = (cJSON *) cJSON_malloc(sizeof(cJSON));
  4. if (node)
  5. memset(node, 0, sizeof(cJSON));   //初始化结构体
  6. return node;
  7. }
  8. cJSON *cJSON_CreateNull(void)
  9. {
  10. cJSON *item = cJSON_New_Item();
  11. if (item)
  12. item->type = cJSON_NULL;
  13. return item;
  14. }
  15. cJSON *cJSON_CreateTrue(void)
  16. {
  17. cJSON *item = cJSON_New_Item();
  18. if (item)
  19. item->type = cJSON_True;
  20. return item;
  21. }
  22. cJSON *cJSON_CreateFalse(void)
  23. {
  24. cJSON *item = cJSON_New_Item();
  25. if (item)
  26. item->type = cJSON_False;
  27. return item;
  28. }
  29. cJSON *cJSON_CreateBool(int b)
  30. {
  31. cJSON *item = cJSON_New_Item();
  32. if (item)
  33. item->type = b ? cJSON_True : cJSON_False;
  34. return item;
  35. }
  36. cJSON *cJSON_CreateNumber(double num)
  37. {
  38. cJSON *item = cJSON_New_Item();
  39. if (item) {
  40. item->type = cJSON_Number;
  41. item->valuedouble = num;
  42. item->valueint = (int) num;
  43. }
  44. return item;
  45. }
  46. cJSON *cJSON_CreateString(const char *string)
  47. {
  48. cJSON *item = cJSON_New_Item();
  49. if (item) {
  50. item->type = cJSON_String;
  51. item->valuestring = cJSON_strdup(string);
  52. }
  53. return item;
  54. }
  55. cJSON *cJSON_CreateArray(void)
  56. {
  57. cJSON *item = cJSON_New_Item();
  58. if (item)
  59. item->type = cJSON_Array;
  60. return item;
  61. }
  62. cJSON *cJSON_CreateObject(void)
  63. {
  64. cJSON *item = cJSON_New_Item();
  65. if (item)
  66. item->type = cJSON_Object;
  67. return item;
  68. }

创建完一个根结点结构体后,接下来就是向根结点中加入元素。

从头文件我们发现,

  • // 创建一个string值为name的cJSON_Null节点,并添加到object
  • #define cJSON_AddNullToObject(object,name)      cJSON_AddItemToObject(object, name, cJSON_CreateNull())
  • // 创建一个string值为name的cJSON_True节点,并添加到object
  • #define cJSON_AddTrueToObject(object,name)      cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
  • // 创建一个string值为name的cJSON_False节点,并添加到object
  • #define cJSON_AddFalseToObject(object,name)     cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
  • // 创建一个string值为name的cJSON_CreateBool节点,并添加到object。b非0为cJSON_True,0为cJSON_False。
  • #define cJSON_AddBoolToObject(object,name,b)    cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
  • // 创建一个string值为name,valuedouble为n,valueint为(int)n的cJSON_Number节点,并添加到object。
  • #define cJSON_AddNumberToObject(object,name,n)  cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
  • // 创建一个string值为name,valuestring为s的cJSON_String节点,并添加到object。
  • #define cJSON_AddStringToObject(object,name,s)  cJSON_AddItemToObject(object, name, cJSON_CreateString(s))

过程是调用cJSON_AddItemToObject()并结合不同的对象类型增加节点名称和子节点。然后在其中调用cJSON_AddItemToArray()函数来添加信息,此函数中判断对象孩子结点是否为NULL,如果是NULL,则直接插入,否则找到最后一个孩子,调用suffix_object()函数添加到双向链表的尾部。

示例图如下:

相关代码如下:

  1. // 将字符串添加进对象
  2. void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item)
  3. {
  4. if (!item)
  5. return;
  6. if (item->string)
  7. cJSON_free(item->string);         // 这个儿子之前有key,先清理
  8. item->string=cJSON_strdup(string);    // 设置key值
  9. cJSON_AddItemToArray(object,item);    // 添加儿子
  10. }
  11. // 将传入的字符串复制一副本并返回新的字符串指针
  12. static char* cJSON_strdup(const char* str)
  13. {
  14. size_t len;
  15. char* copy;
  16. len = strlen(str) + 1;
  17. // 分配空间
  18. if (!(copy = (char*)cJSON_malloc(len)))
  19. return 0;
  20. // 执行复制操作
  21. memcpy(copy,str,len);
  22. // 返回复制的副本
  23. return copy;
  24. }
  25. // 添加节点到object或array中
  26. void cJSON_AddItemToArray(cJSON *array, cJSON *item)
  27. {
  28. cJSON *c=array->child;
  29. if (!item)
  30. return;
  31. if (!c)
  32. {
  33. array->child=item;     // 之前不存在儿子节点,直接添加
  34. }
  35. else
  36. {
  37. while (c && c->next)   // 先找到最后一个儿子
  38. c=c->next;
  39. suffix_object(c,item); // 添加儿子,c是item的兄弟节点
  40. }
  41. }
  42. // array的处理
  43. static void suffix_object(cJSON *prev,cJSON *item)
  44. {
  45. // 两个兄弟的指针互相指向对方
  46. prev->next=item;
  47. item->prev=prev;
  48. }

cjson打印:

cjson打印就是从根对象的结构体开始遍历,得到每个item结点的名称和数据,并经过处理成特定的cjson字符串的输出形式。

cJSON_Print(root)和cJSON_PrintUnformatted(root)
函数都是打印成json字符串的函数,两者的区别就是

cJSON_PrintUnformatted(root)
处理成的字符串里没有\t\n这类的格式,我们在这里以分析cJSON_Print(root)函数

为例,进行分析。

相关函数结构图如下:




相关函数如下:

  1. typedef struct {
  2. char *buffer;
  3. int length;
  4. int offset;
  5. } printbuffer;
  6. static int pow2gt(int x)        /*返回 一个比x的n(其中n是2的幂),并且是最小的幂,说白了就是将一个数后边所有的位都置1然后再+1*/
  7. {
  8. --x;
  9. x |= x >> 1;
  10. x |= x >> 2;
  11. x |= x >> 4;
  12. x |= x >> 8;
  13. x |= x >> 16;
  14. return x + 1;
  15. }
  16. /* ensure 函数 是一个 协助 printbuffer 分配内存的一个函数
  17. * len 表示当前字符串的字符串起始偏移量 即 newbuffer+p->offset 起始的
  18. */
  19. static char* ensure(printbuffer *p,int needed)
  20. {
  21. char *newbuffer;int newsize;
  22. if (!p || !p->buffer) return 0;//传入参数合法性检测
  23. needed+=p->offset;//需要额外分配的内存 也就是偏移量
  24. if (needed<=p->length) return p->buffer+p->offset;//内存够用直接返回
  25. newsize=pow2gt(needed);
  26. newbuffer=(char*)cJSON_malloc(newsize);//malloc出新内存 放buffer里面的内容
  27. if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;}
  28. if (newbuffer) memcpy(newbuffer,p->buffer,p->length);//
  29. cJSON_free(p->buffer);//
  30. p->length=newsize;
  31. p->buffer=newbuffer;
  32. return newbuffer+p->offset;//
  33. }
  34. char *cJSON_Print(cJSON * item)
  35. {
  36. return print_value(item, 0, 1, 0);
  37. }
  38. static char *print_value(cJSON * item, int depth, int fmt, printbuffer * p)
  39. {
  40. char *out = 0;
  41. if (!item)
  42. return 0;
  43. if (p) {
  44. switch ((item->type) & 255) {
  45. case cJSON_NULL:{
  46. out = ensure(p, 5);
  47. if (out)
  48. strcpy(out, "null");
  49. break;
  50. }
  51. case cJSON_False:{
  52. out = ensure(p, 6);
  53. if (out)
  54. strcpy(out, "false");
  55. break;
  56. }
  57. case cJSON_True:{
  58. out = ensure(p, 5);
  59. if (out)
  60. strcpy(out, "true");
  61. break;
  62. }
  63. case cJSON_Number:
  64. out = print_number(item, p);  //打印数字函数
  65. break;
  66. case cJSON_String:
  67. out = print_string(item, p);  //打印字符串函数
  68. break;
  69. case cJSON_Array:
  70. out = print_array(item, depth, fmt, p);  //打印数组函数
  71. break;
  72. case cJSON_Object:
  73. out = print_object(item, depth, fmt, p);  //打印object对象类型的函数
  74. break;
  75. }
  76. } else {
  77. switch ((item->type) & 255) {
  78. case cJSON_NULL:
  79. out = cJSON_strdup("null");
  80. break;
  81. case cJSON_False:
  82. out = cJSON_strdup("false");
  83. break;
  84. case cJSON_True:
  85. out = cJSON_strdup("true");
  86. break;
  87. case cJSON_Number:
  88. out = print_number(item, 0);
  89. break;
  90. case cJSON_String:
  91. out = print_string(item, 0);
  92. break;
  93. case cJSON_Array:
  94. out = print_array(item, depth, fmt, 0);
  95. break;
  96. case cJSON_Object:
  97. out = print_object(item, depth, fmt, 0);
  98. break;
  99. }
  100. }
  101. return out;
  102. }
  103. static char *print_number(cJSON * item, printbuffer * p)  //打印数字函数
  104. {
  105. char *str = 0;
  106. double d = item->valuedouble;
  107. if (d == 0) {
  108. if (p)
  109. str = ensure(p, 2);
  110. else
  111. str = (char *) cJSON_malloc(2); /* special case for 0. */
  112. if (str)
  113. strcpy(str, "0");
  114. } else if (fabs(((double) item->valueint) - d) <= DBL_EPSILON
  115. && d <= INT_MAX && d >= INT_MIN) {
  116. if (p)
  117. str = ensure(p, 21);
  118. else
  119. str = (char *) cJSON_malloc(21);    /* 2 ^ 64 + 1可以用21个字符表示 */
  120. if (str)
  121. sprintf(str, "%d", item->valueint);
  122. } else {
  123. if (p)
  124. str = ensure(p, 64);
  125. else
  126. str = (char *) cJSON_malloc(64);    /* This is a nice tradeoff. */
  127. if (str) {
  128. if (fpclassify(d) != FP_ZERO && !isnormal(d)) //非正常浮点数
  129. sprintf(str, "null");
  130. else if (fabs(floor(d) - d) <= DBL_EPSILON
  131. && fabs(d) < 1.0e60)
  132. sprintf(str, "%.0f", d);
  133. else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9)
  134. sprintf(str, "%e", d);
  135. else
  136. sprintf(str, "%f", d);
  137. }
  138. }
  139. return str;
  140. }
  141. static char *print_string(cJSON * item, printbuffer * p) //打印字符串类型的结点
  142. {
  143. return print_string_ptr(item->valuestring, p);
  144. }
  145. static char *print_string_ptr(const char *str, printbuffer * p)   //打印字符串类型的结点
  146. {
  147. const char *ptr;
  148. char *ptr2, *out;
  149. int len = 0, flag = 0;
  150. unsigned char token;
  151. if (!str) {
  152. if (p)
  153. out = ensure(p, 3);
  154. else
  155. out = (char *) cJSON_malloc(3);
  156. if (!out)
  157. return 0;
  158. strcpy(out, "\"\"");     //字符串为空
  159. return out;
  160. }
  161. for (ptr = str; *ptr; ptr++)
  162. flag |= ((*ptr > 0 && *ptr < 32) || (*ptr == '\"')
  163. || (*ptr == '\\')) ? 1 : 0;
  164. if (!flag) {            //对字符串中不含'\','/',空格等字符的字符处理
  165. len = ptr - str;
  166. if (p)
  167. out = ensure(p, len + 3);
  168. else
  169. out = (char *) cJSON_malloc(len + 3);
  170. if (!out)
  171. return 0;
  172. ptr2 = out;
  173. *ptr2++ = '\"';
  174. strcpy(ptr2, str);
  175. ptr2[len] = '\"';
  176. ptr2[len + 1] = 0;
  177. return out;
  178. }
  179. ptr = str;
  180. while ((token = *ptr) && ++len) {
  181. if (strchr("\"\\\b\f\n\r\t", token))
  182. len++;
  183. else if (token < 32)
  184. len += 5;
  185. ptr++;
  186. }
  187. if (p)
  188. out = ensure(p, len + 3);
  189. else
  190. out = (char *) cJSON_malloc(len + 3);
  191. if (!out)
  192. return 0;
  193. ptr2 = out;
  194. ptr = str;
  195. *ptr2++ = '\"';
  196. while (*ptr) {
  197. if ((unsigned char) *ptr > 31 && *ptr != '\"'
  198. && *ptr != '\\')
  199. *ptr2++ = *ptr++;
  200. else {
  201. *ptr2++ = '\\';
  202. switch (token = *ptr++) {
  203. case '\\':
  204. *ptr2++ = '\\';
  205. break;
  206. case '\"':
  207. *ptr2++ = '\"';
  208. break;
  209. case '\b':
  210. *ptr2++ = 'b';
  211. break;
  212. case '\f':
  213. *ptr2++ = 'f';
  214. break;
  215. case '\n':
  216. *ptr2++ = 'n';
  217. break;
  218. case '\r':
  219. *ptr2++ = 'r';
  220. break;
  221. case '\t':
  222. *ptr2++ = 't';
  223. break;
  224. default:
  225. sprintf(ptr2, "u%04x", token);
  226. ptr2 += 5;
  227. break;  /* escape and print */
  228. }
  229. }
  230. }
  231. *ptr2++ = '\"';
  232. *ptr2++ = 0;
  233. return out;
  234. }
  235. static char *print_array(cJSON * item, int depth, int fmt, printbuffer * p)   //打印数组类型结点函数
  236. {
  237. char **entries;
  238. char *out = 0, *ptr, *ret;
  239. int len = 5;
  240. cJSON *child = item->child;
  241. int numentries = 0, i = 0, fail = 0;
  242. size_t tmplen = 0;
  243. /* 数组里有多少个元素 */
  244. while (child)
  245. numentries++, child = child->next;
  246. /* 明确处理numentries = = 0 */   //处理空数组
  247. if (!numentries) {
  248. if (p)
  249. out = ensure(p, 3);
  250. else
  251. out = (char *) cJSON_malloc(3);
  252. if (out)
  253. strcpy(out, "[]");
  254. return out;
  255. }
  256. if (p) {
  257. /* 组成数组的输出形式 */
  258. i = p->offset;
  259. ptr = ensure(p, 1);
  260. if (!ptr)
  261. return 0;
  262. *ptr = '[';
  263. p->offset++;
  264. child = item->child;
  265. while (child && !fail) {
  266. print_value(child, depth + 1, fmt, p);
  267. p->offset = update(p);
  268. if (child->next) {
  269. len = fmt ? 2 : 1;
  270. ptr = ensure(p, len + 1);
  271. if (!ptr)
  272. return 0;
  273. *ptr++ = ',';
  274. if (fmt)
  275. *ptr++ = ' ';
  276. *ptr = 0;
  277. p->offset += len;
  278. }
  279. child = child->next;
  280. }
  281. ptr = ensure(p, 2);
  282. if (!ptr)
  283. return 0;
  284. *ptr++ = ']';
  285. *ptr = 0;
  286. out = (p->buffer) + i;
  287. } else {
  288. /* 分配一个指针数组存储数组里的每一个元素的打印结果 */
  289. entries =
  290. (char **) cJSON_malloc(numentries * sizeof(char *));
  291. if (!entries)
  292. return 0;
  293. memset(entries, 0, numentries * sizeof(char *));
  294. /* 检索所有结果: */
  295. child = item->child;
  296. while (child && !fail) {
  297. ret = print_value(child, depth + 1, fmt, 0);
  298. entries[i++] = ret;
  299. if (ret)
  300. len += strlen(ret) + 2 + (fmt ? 1 : 0);
  301. else
  302. fail = 1;
  303. child = child->next;
  304. }
  305. if (!fail)
  306. out = (char *) cJSON_malloc(len);
  307. if (!out)
  308. fail = 1;
  309. if (fail) {
  310. for (i = 0; i < numentries; i++)
  311. if (entries[i])
  312. cJSON_free(entries[i]);
  313. cJSON_free(entries);
  314. return 0;
  315. }
  316. /* 组成数组的输出形式. */
  317. *out = '[';
  318. ptr = out + 1;
  319. *ptr = 0;
  320. for (i = 0; i < numentries; i++) {
  321. tmplen = strlen(entries[i]);
  322. memcpy(ptr, entries[i], tmplen);
  323. ptr += tmplen;
  324. if (i != numentries - 1) {
  325. *ptr++ = ',';
  326. if (fmt)
  327. *ptr++ = ' ';
  328. *ptr = 0;
  329. }
  330. cJSON_free(entries[i]);
  331. }
  332. cJSON_free(entries);
  333. *ptr++ = ']';
  334. *ptr++ = 0;
  335. }
  336. return out;
  337. }
  338. /* 打印object类型结点. */
  339. static char *print_object(cJSON * item, int depth, int fmt,  printbuffer * p)
  340. {
  341. char **entries = 0, **names = 0;
  342. char *out = 0, *ptr, *ret, *str;
  343. int len = 7, i = 0, j;
  344. cJSON *child = item->child;
  345. int numentries = 0, fail = 0;
  346. size_t tmplen = 0;
  347. /* 统计有多少个子结点. */
  348. while (child)
  349. numentries++, child = child->next;
  350. /* 明确处理空对象的情况*/
  351. if (!numentries) {
  352. if (p)
  353. out = ensure(p, fmt ? depth + 4 : 3);
  354. else
  355. out = (char *) cJSON_malloc(fmt ? depth + 4 : 3);
  356. if (!out)
  357. return 0;
  358. ptr = out;
  359. *ptr++ = '{';
  360. if (fmt) {
  361. *ptr++ = '\n';
  362. for (i = 0; i < depth; i++)
  363. *ptr++ = '\t';
  364. }
  365. *ptr++ = '}';
  366. *ptr++ = 0;
  367. return out;
  368. }
  369. if (p) {
  370. /* 组成输出形式: */
  371. i = p->offset;
  372. len = fmt ? 2 : 1;
  373. ptr = ensure(p, len + 1);
  374. if (!ptr)
  375. return 0;
  376. *ptr++ = '{';
  377. if (fmt)
  378. *ptr++ = '\n';
  379. *ptr = 0;
  380. p->offset += len;
  381. child = item->child;
  382. depth++;
  383. while (child) {
  384. if (fmt) {
  385. ptr = ensure(p, depth);
  386. if (!ptr)
  387. return 0;
  388. for (j = 0; j < depth; j++)
  389. *ptr++ = '\t';
  390. p->offset += depth;
  391. }
  392. print_string_ptr(child->string, p);
  393. p->offset = update(p);
  394. len = fmt ? 2 : 1;
  395. ptr = ensure(p, len);
  396. if (!ptr)
  397. return 0;
  398. *ptr++ = ':';
  399. if (fmt)
  400. *ptr++ = '\t';
  401. p->offset += len;
  402. print_value(child, depth, fmt, p);
  403. p->offset = update(p);
  404. len = (fmt ? 1 : 0) + (child->next ? 1 : 0);
  405. ptr = ensure(p, len + 1);
  406. if (!ptr)
  407. return 0;
  408. if (child->next)
  409. *ptr++ = ',';
  410. if (fmt)
  411. *ptr++ = '\n';
  412. *ptr = 0;
  413. p->offset += len;
  414. child = child->next;
  415. }
  416. ptr = ensure(p, fmt ? (depth + 1) : 2);
  417. if (!ptr)
  418. return 0;
  419. if (fmt)
  420. for (i = 0; i < depth - 1; i++)
  421. *ptr++ = '\t';
  422. *ptr++ = '}';
  423. *ptr = 0;
  424. out = (p->buffer) + i;
  425. } else {
  426. /*为对象和名称分配空间 */
  427. entries =
  428. (char **) cJSON_malloc(numentries * sizeof(char *));
  429. if (!entries)
  430. return 0;
  431. names =
  432. (char **) cJSON_malloc(numentries * sizeof(char *));
  433. if (!names) {
  434. cJSON_free(entries);
  435. return 0;
  436. }
  437. memset(entries, 0, sizeof(char *) * numentries);
  438. memset(names, 0, sizeof(char *) * numentries);
  439. /* 将所有结果收集到数组: */
  440. child = item->child;
  441. depth++;
  442. if (fmt)
  443. len += depth;
  444. while (child && !fail) {
  445. names[i] = str =
  446. print_string_ptr(child->string, 0);
  447. entries[i++] = ret =
  448. print_value(child, depth, fmt, 0);
  449. if (str && ret)
  450. len +=
  451. strlen(ret) + strlen(str) + 2 +
  452. (fmt ? 2 + depth : 0);
  453. else
  454. fail = 1;
  455. child = child->next;
  456. }
  457. if (!fail)
  458. out = (char *) cJSON_malloc(len);
  459. if (!out)
  460. fail = 1;
  461. if (fail) {
  462. for (i = 0; i < numentries; i++) {
  463. if (names[i])
  464. cJSON_free(names[i]);
  465. if (entries[i])
  466. cJSON_free(entries[i]);
  467. }
  468. cJSON_free(names);
  469. cJSON_free(entries);
  470. return 0;
  471. }
  472. /* 组成输出形式: */
  473. *out = '{';
  474. ptr = out + 1;
  475. if (fmt)
  476. *ptr++ = '\n';
  477. *ptr = 0;
  478. for (i = 0; i < numentries; i++) {
  479. if (fmt)
  480. for (j = 0; j < depth; j++)
  481. *ptr++ = '\t';
  482. tmplen = strlen(names[i]);
  483. memcpy(ptr, names[i], tmplen);
  484. ptr += tmplen;
  485. *ptr++ = ':';
  486. if (fmt)
  487. *ptr++ = '\t';
  488. strcpy(ptr, entries[i]);
  489. ptr += strlen(entries[i]);
  490. if (i != numentries - 1)
  491. *ptr++ = ',';
  492. if (fmt)
  493. *ptr++ = '\n';
  494. *ptr = 0;
  495. cJSON_free(names[i]);
  496. cJSON_free(entries[i]);
  497. }
  498. cJSON_free(names);
  499. cJSON_free(entries);
  500. if (fmt)
  501. for (i = 0; i < depth - 1; i++)
  502. *ptr++ = '\t';
  503. *ptr++ = '}';
  504. *ptr++ = 0;
  505. }
  506. return out;
  507. }

cJSON解析:

首先,调用cJSON_Parse()函数,此函数是一个二次封装函数,其内部为cJSON_ParseWithOpts()函数,该函数用于提取更多的解析选项,如果需要,最后返回解析结束的位置。而在上面的函数中,调用parse_value()函数进行解析,而该函数首先创建cJSON_NewItem()创建节点,用于存放解析的JSON结构数据,然后根据不同的选项,调用解析函数,其为parse_string(),parse_number(),parse_array(),parse_objec()等。

结构图如下:

相关函数如下:

  1. // cJSON解析的二次封装函数
  2. cJSON *cJSON_Parse(const char *value)
  3. {
  4. return cJSON_ParseWithOpts(value,0,0);
  5. }
  6. // 解析对象,创建一个新的根并初始化,返回一个cJSON类型
  7. cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
  8. {
  9. const char *end=0;
  10. cJSON *c=cJSON_New_Item();   //创建一个新结点
  11. ep=0; //ep为全局变量,错误信息就保存在全局字符串指针ep里,调用cJSON_GetErrorPtr()可以得要错误原因的描述字符串
  12. if (!c)
  13. return 0;
  14. end=parse_value(c,skip(value));
  15. if (!end)
  16. {
  17. cJSON_Delete(c);
  18. return 0;
  19. }   /* parse failure. ep is set. */
  20. /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
  21. if (require_null_terminated)
  22. {
  23. end=skip(end);
  24. if (*end)
  25. {
  26. cJSON_Delete(c);
  27. ep=end;
  28. return 0;
  29. }
  30. }
  31. if (return_parse_end)
  32. *return_parse_end=end;
  33. return c;
  34. }
  35. // 解析器核心函数
  36. static const char *parse_value(cJSON *item,const char *value)
  37. {
  38. if (!value)
  39. return 0;   /* Fail on null. */
  40. if (!strncmp(value,"null",4))
  41. {
  42. item->type=cJSON_NULL;
  43. return value+4;
  44. }
  45. if (!strncmp(value,"false",5))
  46. {
  47. item->type=cJSON_False;
  48. return value+5;
  49. }
  50. if (!strncmp(value,"true",4))
  51. {
  52. item->type=cJSON_True;
  53. item->valueint=1;
  54. return value+4;
  55. }
  56. if (*value=='\"')
  57. {
  58. return parse_string(item,value);
  59. }
  60. if (*value=='-' || (*value>='0' && *value<='9'))
  61. {
  62. return parse_number(item,value);
  63. }
  64. if (*value=='[')
  65. {
  66. return parse_array(item,value);
  67. }
  68. if (*value=='{')
  69. {
  70. return parse_object(item,value);
  71. }
  72. ep=value;
  73. return 0;   /* failure. */
  74. }
  75. static const char *parse_string(cJSON *item,const char *str)   //解析字符串函数
  76. {
  77. const char *ptr=str+1;
  78. char *ptr2;
  79. char *out;
  80. int len=0;
  81. unsigned uc,uc2;
  82. if (*str!='\"')    // 不是字符串情况
  83. {
  84. ep=str;
  85. return 0;
  86. }   /* not a string! */
  87. while (*ptr!='\"' && *ptr && ++len)
  88. if (*ptr++ == '\\')
  89. ptr++;   // 跳出前面的引用
  90. out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
  91. if (!out)
  92. return 0;
  93. ptr=str+1;
  94. ptr2=out;
  95. while (*ptr!='\"' && *ptr)
  96. {
  97. if (*ptr!='\\')
  98. *ptr2++=*ptr++;
  99. else
  100. {
  101. ptr++;
  102. switch (*ptr)
  103. {
  104. case 'b': *ptr2++='\b'; break;
  105. case 'f': *ptr2++='\f'; break;
  106. case 'n': *ptr2++='\n'; break;
  107. case 'r': *ptr2++='\r'; break;
  108. case 't': *ptr2++='\t'; break;
  109. case 'u':    /* transcode utf16 to utf8. */
  110. uc=parse_hex4(ptr+1);
  111. ptr+=4; /* get the unicode char. */
  112. if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0)
  113. break;  /* check for invalid.   */
  114. if (uc>=0xD800 && uc<=0xDBFF)   /* UTF16 surrogate pairs.   */
  115. {
  116. if (ptr[1]!='\\' || ptr[2]!='u')
  117. break;  /* missing second-half of surrogate.    */
  118. uc2=parse_hex4(ptr+3);ptr+=6;
  119. if (uc2<0xDC00 || uc2>0xDFFF)
  120. break;  /* invalid second-half of surrogate.    */
  121. uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
  122. }
  123. len=4;
  124. if (uc<0x80)
  125. len=1;
  126. else if (uc<0x800)
  127. len=2;
  128. else if (uc<0x10000)
  129. len=3;
  130. ptr2+=len;
  131. switch (len)
  132. {
  133. case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
  134. case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
  135. case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
  136. case 1: *--ptr2 =(uc | firstByteMark[len]);
  137. }
  138. ptr2+=len;
  139. break;
  140. default:  *ptr2++=*ptr; break;
  141. }
  142. ptr++;
  143. }
  144. }
  145. *ptr2=0;
  146. if (*ptr=='\"') ptr++;
  147. item->valuestring=out;
  148. item->type=cJSON_String;
  149. return ptr;
  150. }
  151. // 跳过这些空格,这里跳过了ascii值小于32的。
  152. static const char *skip(const char *in)
  153. {
  154. while (in && *in && (unsigned char)*in<=32)
  155. in++;
  156. return in;
  157. }
  158. // parse_number函数功能:解析数字,对输入的文本生成一个数字,并填充结果项,传入参数有两
  159. // 个,这里先只关注num,返回值是一个字符串
  160. static const char *parse_number(cJSON *item,const char *num)
  161. {
  162. double n=0,sign=1,scale=0;
  163. int subscale=0,signsubscale=1;
  164. if (*num=='-') sign=-1,num++;      // 判断数字是否是有符号数字
  165. if (*num=='0') num++;                  // 判断数字是否为0
  166. if (*num>='1' && *num<='9')
  167. do                               // 转换数字
  168. n=(n*10.0)+(*num++ -'0');
  169. while (*num>='0' && *num<='9');
  170. if (*num=='.' && num[1]>='0' && num[1]<='9') // 对小数点后边的部分进行处理,scale记录小数点后边的位数
  171. {
  172. num++;
  173. do
  174. n=(n*10.0)+(*num++ -'0'),scale--;       // scale为小数点后的位数
  175. while (*num>='0' && *num<='9');
  176. }
  177. if (*num=='e' || *num=='E')        // 是否为指数,科学计数法
  178. {
  179. num++;
  180. if (*num=='+')                 // 判断指数后边幂的正负号
  181. num++;
  182. else if (*num=='-')
  183. signsubscale=-1,num++;
  184. while (*num>='0' && *num<='9') // 处理指数后边10的幂
  185. subscale=(subscale*10)+(*num++ - '0');
  186. }
  187. // 将字符串转换为相应的数值
  188. n=sign*n*pow(10.0,(scale+subscale*signsubscale));   /* number = +/- number.fraction * 10^+/- exponent */
  189. item->valuedouble=n;             // 将算出来的值存入缓存
  190. item->valueint=(int)n;           // 将算出来的值存入缓存
  191. item->type=cJSON_Number;         // 目标类型为数字
  192. return num;
  193. }
  194. // 从输入文本中构建array
  195. static const char *parse_array(cJSON *item,const char *value)
  196. {
  197. cJSON *child;
  198. if (*value!='[')    {ep=value;return 0;}    /* not an array! */
  199. item->type=cJSON_Array;
  200. value=skip(value+1);
  201. if (*value==']') return value+1;    /* empty array. */
  202. item->child=child=cJSON_New_Item();
  203. if (!item->child) return 0;      /* memory fail */
  204. value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
  205. if (!value) return 0;
  206. while (*value==',')
  207. {
  208. cJSON *new_item;
  209. if (!(new_item=cJSON_New_Item())) return 0;     /* memory fail */
  210. child->next=new_item;new_item->prev=child;child=new_item;
  211. value=skip(parse_value(child,skip(value+1)));
  212. if (!value) return 0;   /* memory fail */
  213. }
  214. if (*value==']') return value+1;    /* end of array */
  215. ep=value;return 0;  /* malformed. */
  216. }
  217. // 从输入文本中构建object
  218. static const char *parse_object(cJSON *item,const char *value)
  219. {
  220. cJSON *child;
  221. if (*value!='{')    {ep=value;return 0;}    /* not an object! */
  222. item->type=cJSON_Object;
  223. value=skip(value+1);
  224. if (*value=='}') return value+1;    /* empty array. */
  225. item->child=child=cJSON_New_Item();
  226. if (!item->child) return 0;
  227. value=skip(parse_string(child,skip(value)));
  228. if (!value) return 0;
  229. child->string=child->valuestring;child->valuestring=0;
  230. if (*value!=':') {ep=value;return 0;}   /* fail! */
  231. value=skip(parse_value(child,skip(value+1)));   /* skip any spacing, get the value. */
  232. if (!value) return 0;
  233. while (*value==',')
  234. {
  235. cJSON *new_item;
  236. if (!(new_item=cJSON_New_Item()))   return 0; /* memory fail */
  237. child->next=new_item;new_item->prev=child;child=new_item;
  238. value=skip(parse_string(child,skip(value+1)));
  239. if (!value) return 0;
  240. child->string=child->valuestring;child->valuestring=0;
  241. if (*value!=':') {ep=value;return 0;}   /* fail! */
  242. value=skip(parse_value(child,skip(value+1)));   /* skip any spacing, get the value. */
  243. if (!value) return 0;
  244. }
  245. if (*value=='}') return value+1;    /* end of array */
  246. ep=value;return 0;  /* malformed. */
  247. }
  248. // 将十六进制的字符串转换为数字表示!
  249. static unsigned parse_hex4(const char *str)
  250. {
  251. unsigned h=0;
  252. if (*str>='0' && *str<='9')
  253. h+=(*str)-'0';
  254. else if (*str>='A' && *str<='F')
  255. h+=10+(*str)-'A';
  256. else if (*str>='a' && *str<='f')
  257. h+=10+(*str)-'a';
  258. else
  259. return 0;
  260. h=h<<4;str++;
  261. if (*str>='0' && *str<='9')
  262. h+=(*str)-'0';
  263. else if (*str>='A' && *str<='F')
  264. h+=10+(*str)-'A';
  265. else if (*str>='a' && *str<='f')
  266. h+=10+(*str)-'a';
  267. else
  268. return 0;
  269. h=h<<4;str++;
  270. if (*str>='0' && *str<='9')
  271. h+=(*str)-'0';
  272. else if (*str>='A' && *str<='F')
  273. h+=10+(*str)-'A';
  274. else if (*str>='a' && *str<='f')
  275. h+=10+(*str)-'a';
  276. else
  277. return 0;
  278. h=h<<4;str++;
  279. if (*str>='0' && *str<='9')
  280. h+=(*str)-'0';
  281. else if (*str>='A' && *str<='F')
  282. h+=10+(*str)-'A';
  283. else if (*str>='a' && *str<='f')
  284. h+=10+(*str)-'a';
  285. else
  286. return 0;
  287. return h;
  288. }

cJSON内存管理:

hook管理函数:

在 c 语言中内存一般是 malloc 和 free 的。
为了方便用户自由的管理内存, cjson 使用 Hook 技术来让使用者可以自定义内存管理函数。
即用户自定义 malloc 和 free.
具体实现方式可以参考下面的代码, 默认使用系统的 malloc 和 free 函数, 用过 cJSON_InitHooks 函数可以替换成用户自定义的 malloc 和 free 函数。

  1. typedef struct cJSON_Hooks
  2. {
  3. void *(*malloc_fn)(size_t sz);
  4. void (*free_fn)(void *ptr);
  5. } cJSON_Hooks;
  6. // 对cJSON提供的分配,再分配,释放内存初始化函数
  7. extern void cJSON_InitHooks(cJSON_Hooks* hooks);
  8. // 默认将分配和释放空间函数指针指向malloc和free
  9. static void *(*cJSON_malloc)(size_t sz) = malloc;
  10. static void (*cJSON_free)(void *ptr) = free;
  11. // 其使用Hook技术来让使用者可以自定义内存管理函数。其中默认系统使用的内存分配和释放函数是malloc
  12. // 和free函数,利用cJSON_InitHooks函数可以替换成用户自定义的malloc和free函数。
  13. void cJSON_InitHooks(cJSON_Hooks* hooks)
  14. {
  15. // 如果未定义,则使用默认的malloc和free函数
  16. if (!hooks) { /* Reset hooks */
  17. cJSON_malloc = malloc;
  18. cJSON_free = free;
  19. return;
  20. }
  21. // 定义了,则使用用户自定义的malloc和free函数
  22. cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
  23. cJSON_free   = (hooks->free_fn)?hooks->free_fn:free;
  24. }

cJSON的删除:

删除节点很简单, 先删除儿子,然后清理内存即可。
总结一下就是对于 object 和 array 需要先删除儿子,然后删除自己。
对于 字符串, 需要先释放字符串的内存, 再释放自己这块内存。
对于其他节点,直接释放自己这块内存。

  1. /*删除结点函数*/
  2. void cJSON_Delete(cJSON *c) {
  3. cJSON *next;
  4. while (c) {
  5. next=c->next;
  6. if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
  7. if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
  8. if (c->string) cJSON_free(c->string);
  9. cJSON_free(c);
  10. c=next;
  11. }
  12. }
  13. /*删除儿子结点函数*/
  14. 删除也是从 array 和 object 中删除,实现就比较简洁了。
  15. void   cJSON_DeleteItemFromArray(cJSON *array,int which) {
  16. cJSON_Delete(cJSON_DetachItemFromArray(array,which));
  17. }
  18. void   cJSON_DeleteItemFromObject(cJSON *object,const char *string) {
  19. cJSON_Delete(cJSON_DetachItemFromObject(object,string));
  20. }

Detach 又是什么东西呢?
我们把一个节点从 json 树中删除, 但是不释放内存,而是先保留这个节点的指针, 这样储存在这个节点的信息都保留了下来。
接下来我们就可以做很多事了, 合适的时候添加到其他对象中, 合适的时候释放内存。
比如上面的 delete 函数, 就需要真实的删除了, 这个时候我们删除即可。
而 detach 实现也比较简单, 只是少了一步删除操作。

  1. // 节点从双向链表中删除即可
  2. cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {
  3. cJSON *c=array->child;
  4. while (c && which>0) c=c->next,which--;
  5. if (!c) return 0;
  6. if (c->prev) c->prev->next=c->next;
  7. if (c->next) c->next->prev=c->prev;
  8. if (c==array->child) array->child=c->next;
  9. c->prev=c->next=0;
  10. return c;
  11. }
  12. cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {
  13. int i=0;
  14. cJSON *c=object->child;
  15. while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;
  16. if (c) return cJSON_DetachItemFromArray(object,i);
  17. return 0;
  18. }

其他函数:

前面我们已经将json功能分为三大块进行了解析,现在把剩余的一些函数贴上,这些函数单独分析即可。

    1. // 返回节点的个数
    2. int cJSON_GetArraySize(cJSON *array)
    3. {
    4. cJSON *c=array->child;
    5. int i=0;
    6. while(c)
    7. i++,c=c->next;
    8. return i;
    9. }
    10. // 返回array中第item个节点的地址
    11. cJSON *cJSON_GetArrayItem(cJSON *array,int item)
    12. {
    13. cJSON *c=array->child;
    14. while (c && item>0)
    15. item--,c=c->next;
    16. return c;
    17. }
    18. // 返回Object中第item个节点的地址
    19. cJSON *cJSON_GetObjectItem(cJSON *object,const char *string)
    20. {
    21. cJSON *c=object->child;
    22. while (c && cJSON_strcasecmp(c->string,string))
    23. c=c->next;
    24. return c;
    25. }
    26. //在链表中插入一个新结点
    27. void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem)
    28. {
    29. cJSON *c=array->child;
    30. // 找到which位置
    31. while (c && which>0)
    32. c=c->next,which--;
    33. // 添加新的节点到array中
    34. if (!c)
    35. {
    36. cJSON_AddItemToArray(array,newitem);
    37. return;
    38. }
    39. // 将链表节点进行挂接
    40. newitem->next=c;
    41. newitem->prev=c->prev;
    42. c->prev=newitem;
    43. // 处理arrya的孩子节点
    44. if (c==array->child)
    45. array->child=newitem;
    46. else
    47. newitem->prev->next=newitem;
    48. }
    49. // 替换节点操作,用新的节点替换原有的某一个节点
    50. void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem)
    51. {
    52. cJSON *c=array->child;
    53. // 找到which位置
    54. while (c && which>0)
    55. c=c->next,which--;
    56. if (!c)
    57. return;
    58. // 进行挂接
    59. newitem->next=c->next;
    60. newitem->prev=c->prev;
    61. // 处理NULL情况
    62. if (newitem->next)
    63. newitem->next->prev=newitem;
    64. // 处理孩子节点
    65. if (c==array->child)
    66. array->child=newitem;
    67. else
    68. newitem->prev->next=newitem;
    69. c->next=c->prev=0;
    70. // 删除替换的节点
    71. cJSON_Delete(c);
    72. }
    73. // 替换节点操作
    74. // 用原有节点替换现有节点
    75. void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem)
    76. {
    77. int i=0;
    78. cJSON *c=object->child;
    79. while(c && cJSON_strcasecmp(c->string,string))
    80. i++,c=c->next;
    81. if(c)
    82. {
    83. newitem->string=cJSON_strdup(string);
    84. cJSON_ReplaceItemInArray(object,i,newitem);
    85. }
    86. }
    87. // 拷贝副本操作
    88. cJSON *cJSON_Duplicate(cJSON *item,int recurse)
    89. {
    90. cJSON *newitem,*cptr,*nptr=0,*newchild;
    91. /* Bail on bad ptr */
    92. if (!item)
    93. return 0;
    94. /* Create new item */
    95. newitem=cJSON_New_Item();
    96. if (!newitem)
    97. return 0;
    98. /* Copy over all vars */
    99. newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
    100. if (item->valuestring)
    101. {
    102. newitem->valuestring=cJSON_strdup(item->valuestring);
    103. if (!newitem->valuestring)
    104. {
    105. cJSON_Delete(newitem);
    106. return 0;
    107. }
    108. }
    109. if (item->string)
    110. {
    111. newitem->string=cJSON_strdup(item->string);
    112. if (!newitem->string)
    113. {
    114. cJSON_Delete(newitem);
    115. return 0;
    116. }
    117. }
    118. /* If non-recursive, then we're done! */
    119. if (!recurse)
    120. return newitem;
    121. /* Walk the ->next chain for the child. */
    122. cptr=item->child;
    123. while (cptr)
    124. {
    125. newchild=cJSON_Duplicate(cptr,1);       /* Duplicate (with recurse) each item in the ->next chain */
    126. if (!newchild)
    127. {
    128. cJSON_Delete(newitem);
    129. return 0;
    130. }
    131. if (nptr)
    132. {
    133. nptr->next=newchild,newchild->prev=nptr;
    134. nptr=newchild;
    135. }   /* If newitem->child already set, then crosswire ->prev and ->next and move on */
    136. else
    137. {
    138. newitem->child=newchild;
    139. nptr=newchild;
    140. }                   /* Set newitem->child and move to it */
    141. cptr=cptr->next;
    142. }
    143. return newitem;
    144. }

原文出处

【转】 cJSON 源码解析的更多相关文章

  1. cJSON源码分析

    JSON (JavaScript Object Notation) 是一种常见使用的轻量级的数据交换格式,既有利于人工读写,也方便于机器的解析和生成. 关于JSON格式的定义,参看网站[1].在该网站 ...

  2. 【转】cJSON 源码分析

    cJSON源码分析 简介 由于C语言汇总,没有直接的字典,字符串数组等数据结构,所以要借助结构体定义,处理json. JSON是一种轻量级的数据交换格式.JSON采用完全独立与语言的文本格式,易于人阅 ...

  3. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  4. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  5. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  6. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  7. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  8. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

  9. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

随机推荐

  1. js 稍微判断下浏览器 pc 还是手机

        function isMobile() {    var a=navigator.userAgent;   var ref=/.*(Android|iPhone|SymbianOS|iPad| ...

  2. 【转】JS中,中国标准时间转化为yyyy-MM-dd

    'Thu May 12 2016 08:00:00 GMT+0800 (中国标准时间)'--此格式日期无法传到java后台,须格式化,方法如下 var d = new Date('Thu May 12 ...

  3. React + js-xlsx构建Excel文件上传预览功能

    首先要准备react开发环境以及js-xlsx插件 1. 此处省略安装react安装步骤 2.下载js-xlsx插件 yarn add xlsx 或者 npm install xlsx 在项目中引入 ...

  4. Linux性能评测工具之一:gprof篇

    这些天自己试着对项目作一些压力测试和性能优化,也对用过的测试工具作一些总结,并把相关的资料作一个汇总,以便以后信手拈来! 1 简介 改进应用程序的性能是一项非常耗时耗力的工作,但是究竟程序中是哪些函数 ...

  5. Flutter - 自动生成Android & iOS图标

    对于要发布的app来说,做图标是一个麻烦的事,你需要知道N个图标的分辨率,然后用PhotoShop一个个修改导出. PS好图标之后,按照各自的位置放进去. ********************** ...

  6. YY:2018互联网创业公司应看清的事情

    潮流,技术,生活方式,盈利模式,消费人群几乎每年都在改变,2018,你看到的是怎样的一盘棋. 2018年是个很好的数字,很多互联网公司寄予希望在这个幸运数字年头奋起一搏,拿到一份可观的酬金.特别是一些 ...

  7. 第七组团队项目——专业课程资源共享平台——需求分析&原型设计

    一.项目目标.定位需求: (1)目标:在教师.学生之间建立一个综合的.全面的.快捷的.高效的免费课程和学习资源共享.交流与推荐的开放性平台,实现多维和动态的推荐与分类检索服务. (2)定位:学生与教师 ...

  8. 理解java的三大特性之继承

    学习来源:http://www.cnblogs.com/chenssy/p/3354884.html default 默认权限(包权限-同一个包可以访问) private 私有(类内部可以使用,继承的 ...

  9. hadoop伪分布式安装之Linux环境准备

    Hadoop伪分布式安装之Linux环境准备 一.软件版本 VMare Workstation Pro 14 CentOS 7 32/64位 二.实现Linux服务器联网功能 网络适配器双击选择VMn ...

  10. 业务-----修改Service常用逻辑

    注意:修改时唯一属性不能重复 //num==null 时,没有修改Num,不用考虑重复问题.//num!=null 时,修改了num.考虑重复问题 if(!StringUtils.isEmpty(re ...