分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 教程案例

【转】 cJSON 源码解析

发布时间:2023-09-06 01:33责任编辑:苏小强关键词:暂无标签

关于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. #ifndefcJSON__h
  2. #definecJSON__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. typedefstructcJSON
  3. {
  4. structcJSON*next,*prev;//同一级的元素使用双向列表存储
  5. structcJSON*child;//如果是一个object或array的话,child为第一个儿子的指针
  6. inttype;//value的类型
  7. char*valuestring;//如果这个value是字符串类型,则此处为字符串值
  8. intvalueint;//如果是数字的话,整数值
  9. doublevaluedouble;//如果是数字的话,读点数值
  10. char*string;//json对象的名称
  11. }cJSON;






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

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


示例图如下:



cjson的类型宏定义:

  1. /*cJSONTypes:*/
  2. #definecJSON_False(1<<0)
  3. #definecJSON_True(1<<1)
  4. #definecJSON_NULL(1<<2)
  5. #definecJSON_Number(1<<3)
  6. #definecJSON_String(1<<4)
  7. #definecJSON_Array(1<<5)
  8. #definecJSON_Object(1<<6)
  9. #definecJSON_IsReference256
  10. #definecJSON_StringIsConst512


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


cjson的创建:

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

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

相关函数如下:

  1. staticcJSON*cJSON_New_Item(void)//创建json结构体
  2. {
  3. cJSON*node=(cJSON*)cJSON_malloc(sizeof(cJSON));
  4. if(node)
  5. memset(node,0,sizeof(cJSON));//初始化结构体
  6. returnnode;
  7. }
  8. cJSON*cJSON_CreateNull(void)
  9. {
  10. cJSON*item=cJSON_New_Item();
  11. if(item)
  12. item->type=cJSON_NULL;
  13. returnitem;
  14. }
  15. cJSON*cJSON_CreateTrue(void)
  16. {
  17. cJSON*item=cJSON_New_Item();
  18. if(item)
  19. item->type=cJSON_True;
  20. returnitem;
  21. }
  22. cJSON*cJSON_CreateFalse(void)
  23. {
  24. cJSON*item=cJSON_New_Item();
  25. if(item)
  26. item->type=cJSON_False;
  27. returnitem;
  28. }
  29. cJSON*cJSON_CreateBool(intb)
  30. {
  31. cJSON*item=cJSON_New_Item();
  32. if(item)
  33. item->type=b?cJSON_True:cJSON_False;
  34. returnitem;
  35. }
  36. cJSON*cJSON_CreateNumber(doublenum)
  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. returnitem;
  45. }
  46. cJSON*cJSON_CreateString(constchar*string)
  47. {
  48. cJSON*item=cJSON_New_Item();
  49. if(item){
  50. item->type=cJSON_String;
  51. item->valuestring=cJSON_strdup(string);
  52. }
  53. returnitem;
  54. }
  55. cJSON*cJSON_CreateArray(void)
  56. {
  57. cJSON*item=cJSON_New_Item();
  58. if(item)
  59. item->type=cJSON_Array;
  60. returnitem;
  61. }
  62. cJSON*cJSON_CreateObject(void)
  63. {
  64. cJSON*item=cJSON_New_Item();
  65. if(item)
  66. item->type=cJSON_Object;
  67. returnitem;
  68. }


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

从头文件我们发现,

  • //创建一个string值为name的cJSON_Null节点,并添加到object
  • #definecJSON_AddNullToObject(object,name)cJSON_AddItemToObject(object,name,cJSON_CreateNull())
  • //创建一个string值为name的cJSON_True节点,并添加到object
  • #definecJSON_AddTrueToObject(object,name)cJSON_AddItemToObject(object,name,cJSON_CreateTrue())
  • //创建一个string值为name的cJSON_False节点,并添加到object
  • #definecJSON_AddFalseToObject(object,name)cJSON_AddItemToObject(object,name,cJSON_CreateFalse())
  • //创建一个string值为name的cJSON_CreateBool节点,并添加到object。b非0为cJSON_True,0为cJSON_False。
  • #definecJSON_AddBoolToObject(object,name,b)cJSON_AddItemToObject(object,name,cJSON_CreateBool(b))
  • //创建一个string值为name,valuedouble为n,valueint为(int)n的cJSON_Number节点,并添加到object。
  • #definecJSON_AddNumberToObject(object,name,n)cJSON_AddItemToObject(object,name,cJSON_CreateNumber(n))
  • //创建一个string值为name,valuestring为s的cJSON_String节点,并添加到object。
  • #definecJSON_AddStringToObject(object,name,s)cJSON_AddItemToObject(object,name,cJSON_CreateString(s))


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

示例图如下:



相关代码如下:

  1. //将字符串添加进对象
  2. voidcJSON_AddItemToObject(cJSON*object,constchar*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. staticchar*cJSON_strdup(constchar*str)
  13. {
  14. size_tlen;
  15. char*copy;
  16. len=strlen(str)+1;
  17. //分配空间
  18. if(!(copy=(char*)cJSON_malloc(len)))
  19. return0;
  20. //执行复制操作
  21. memcpy(copy,str,len);
  22. //返回复制的副本
  23. returncopy;
  24. }
  25. //添加节点到object或array中
  26. voidcJSON_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. staticvoidsuffix_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. typedefstruct{
  2. char*buffer;
  3. intlength;
  4. intoffset;
  5. }printbuffer;
  6. staticintpow2gt(intx)/*返回一个比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. returnx+1;
  15. }
  16. /*ensure函数是一个协助printbuffer分配内存的一个函数
  17. *len表示当前字符串的字符串起始偏移量即newbuffer+p->offset起始的
  18. */
  19. staticchar*ensure(printbuffer*p,intneeded)
  20. {
  21. char*newbuffer;intnewsize;
  22. if(!p||!p->buffer)return0;//传入参数合法性检测
  23. needed+=p->offset;//需要额外分配的内存也就是偏移量
  24. if(needed<=p->length)returnp->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;return0;}
  28. if(newbuffer)memcpy(newbuffer,p->buffer,p->length);//
  29. cJSON_free(p->buffer);//
  30. p->length=newsize;
  31. p->buffer=newbuffer;
  32. returnnewbuffer+p->offset;//
  33. }
  34. char*cJSON_Print(cJSON*item)
  35. {
  36. returnprint_value(item,0,1,0);
  37. }
  38. staticchar*print_value(cJSON*item,intdepth,intfmt,printbuffer*p)
  39. {
  40. char*out=0;
  41. if(!item)
  42. return0;
  43. if(p){
  44. switch((item->type)&255){
  45. casecJSON_NULL:{
  46. out=ensure(p,5);
  47. if(out)
  48. strcpy(out,"null");
  49. break;
  50. }
  51. casecJSON_False:{
  52. out=ensure(p,6);
  53. if(out)
  54. strcpy(out,"false");
  55. break;
  56. }
  57. casecJSON_True:{
  58. out=ensure(p,5);
  59. if(out)
  60. strcpy(out,"true");
  61. break;
  62. }
  63. casecJSON_Number:
  64. out=print_number(item,p);//打印数字函数
  65. break;
  66. casecJSON_String:
  67. out=print_string(item,p);//打印字符串函数
  68. break;
  69. casecJSON_Array:
  70. out=print_array(item,depth,fmt,p);//打印数组函数
  71. break;
  72. casecJSON_Object:
  73. out=print_object(item,depth,fmt,p);//打印object对象类型的函数
  74. break;
  75. }
  76. }else{
  77. switch((item->type)&255){
  78. casecJSON_NULL:
  79. out=cJSON_strdup("null");
  80. break;
  81. casecJSON_False:
  82. out=cJSON_strdup("false");
  83. break;
  84. casecJSON_True:
  85. out=cJSON_strdup("true");
  86. break;
  87. casecJSON_Number:
  88. out=print_number(item,0);
  89. break;
  90. casecJSON_String:
  91. out=print_string(item,0);
  92. break;
  93. casecJSON_Array:
  94. out=print_array(item,depth,fmt,0);
  95. break;
  96. casecJSON_Object:
  97. out=print_object(item,depth,fmt,0);
  98. break;
  99. }
  100. }
  101. returnout;
  102. }
  103. staticchar*print_number(cJSON*item,printbuffer*p)//打印数字函数
  104. {
  105. char*str=0;
  106. doubled=item->valuedouble;
  107. if(d==0){
  108. if(p)
  109. str=ensure(p,2);
  110. else
  111. str=(char*)cJSON_malloc(2);/*specialcasefor0.*/
  112. if(str)
  113. strcpy(str,"0");
  114. }elseif(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);/*Thisisanicetradeoff.*/
  127. if(str){
  128. if(fpclassify(d)!=FP_ZERO&&!isnormal(d))//非正常浮点数
  129. sprintf(str,"null");
  130. elseif(fabs(floor(d)-d)<=DBL_EPSILON
  131. &&fabs(d)<1.0e60)
  132. sprintf(str,"%.0f",d);
  133. elseif(fabs(d)<1.0e-6||fabs(d)>1.0e9)
  134. sprintf(str,"%e",d);
  135. else
  136. sprintf(str,"%f",d);
  137. }
  138. }
  139. returnstr;
  140. }
  141. staticchar*print_string(cJSON*item,printbuffer*p)//打印字符串类型的结点&nbs
我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved