酷勤网 – 程序员的那点事!

当前位置:首页 > 编程 > 开源风暴 > 正文

cjson 源码阅读笔记

浏览次数: tiankonguse .github..io 2014年12月24日 字号:
之前看了sphinx的源码之后,心中大概有了json实现的原型,但是没想到c语言的json实现是如此的暴力简单。

前言

cjson 的代码只有 1000+ 行, 而且只是简单的几个函数的调用。

而且 cjson 还有很多不完善的地方, 推荐大家看完之后自己实现一个 封装好的功能完善的 cjson 程序。

json 基本信息

在阅读 json 之前, 建议阅读一下json 的官方介绍

如果上面的英文吓到你了的话, 可以看看这个中文翻译版本.

我的 这个 cjson 是从官网指定的地方下载的ourceforge.

在看完官网的介绍后,我们知道 json 的 value 存在这么几种类型: 对象, 数组, 字符串, 数字, true, false, null。

其中对象是一个 key-value 的集合, 而数组是一些 value 的有序列表。

于是 cjson 中在 头文件中定义了 这些类型的数字编号和 cJSON value 的结构体。

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

对于上面的 define , 如果是我的话,会选择 emnu 来实现这个类型的定义。

例如

  1. enum {cJSON_False, cJSON_True, cJSON_NULL, cJSON_Number, cJSON_String, cJSON_Array, cJSON_Object, cJSON_IsReference=256};

然后是 json 一个 value 的结构,看注释也都可以明白干什么的。

  1. /* The cJSON structure: */
  2. typedef struct cJSON {
  3. struct cJSON *next,*prev; /同一级的元素使用双向列表储存/
  4. struct cJSON *child; /* 如果是个 object 或 array 的话,第一个儿子的指针 */
  5.  
  6. int type; /* value 的类型 */
  7.  
  8. char *valuestring; /* 如果这个 value 是 字符串 的话,字符串值 */
  9. int valueint; /* 如果是数字的话,整数值 */
  10. double valuedouble; /* 如果是数字的话,浮点数值 */
  11.  
  12. char *string; /* 如果是对象的 key-value 元素的话, key 值 */
  13. } cJSON;

json 内存管理

hook 管理函数

在 c 语言中内存一般是 malloc 和 free 的。

为了方便用户自由的管理内存, cjson 使用 Hook 技术来让使用者可以自定义内存管理函数。

即用户自定义 malloc 和 free.

具体实现方式可以参考下面的代码, 默认使用系统的 malloc 和 free 函数, 用过 cJSON_InitHooks 函数可以替换成用户自定义的 malloc 和 free 函数。

  1. typedef struct cJSON_Hooks {
  2. void *(*malloc_fn)(size_t sz);
  3. void (*free_fn)(void *ptr);
  4. } cJSON_Hooks;
  5.  
  6. static void *(*cJSON_malloc)(size_t sz) = malloc;
  7. static void (*cJSON_free)(void *ptr) = free;
  8.  
  9. void cJSON_InitHooks(cJSON_Hooks* hooks) {
  10. if (!hooks) { /* Reset hooks */
  11. cJSON_malloc = malloc;
  12. cJSON_free = free;
  13. return;
  14. }
  15.  
  16. cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
  17. cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
  18. }

创建节点

有了内存管理函数,我们就可以生成我们的 value 节点了。

  1. /* Internal constructor. */
  2. static cJSON *cJSON_New_Item(void) {
  3. cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
  4. if (node) memset(node,0,sizeof(cJSON));
  5. return node;
  6. }

然后通过再设置具体的类型即生成对应类型的节点。

  1. /* Create basic types: */
  2. cJSON *cJSON_CreateNull(void) {
  3. cJSON *item=cJSON_New_Item();
  4. if(item)item->type=cJSON_NULL;
  5. return item;
  6. }
  7. cJSON *cJSON_CreateTrue(void);
  8. cJSON *cJSON_CreateFalse(void);
  9. cJSON *cJSON_CreateBool(int b) {
  10. cJSON *item=cJSON_New_Item();
  11. if(item)item->type=b?cJSON_True:cJSON_False;
  12. return item;
  13. }
  14. cJSON *cJSON_CreateNumber(double num) {
  15. cJSON *item=cJSON_New_Item();
  16. if(item) {
  17. item->type=cJSON_Number;
  18. item->valuedouble=num;
  19. item->valueint=(int)num;
  20. }
  21. return item;
  22. }
  23. cJSON *cJSON_CreateString(const char *string) {
  24. cJSON *item=cJSON_New_Item();
  25. if(item) {
  26. item->type=cJSON_String;
  27. item->valuestring=cJSON_strdup(string);
  28. }
  29. return item;
  30. }
  31. cJSON *cJSON_CreateArray(void);
  32. cJSON *cJSON_CreateObject(void);

上面我们看到一个 cJSON_strdup 函数, 简单的理解就是复制字符串,返回新的字符串的指针。

删除节点

删除节点很简单, 先删除儿子,然后清理内存即可。

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

  1. /* Delete a cJSON structure. */
  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. }

节点操作

有了内存管理,我们就可以得到一些列不同类型的节点了。

然后我们通过节点操作就可以把这些节点连接起来,组成一棵树。

是的,所有的json 都可以理解为一颗有根树。

而节点操作有把加点 a 添加为节点 b 的儿子, 把节点 b 从节点 a 的儿子中删除。

或者修改节点 a 的值或者查询节点 a 的值。

对,就是传说中的增删改查

添加儿子节点

添加儿子节点有两种情况,一种是给 object 增加儿子, 一种是给 array 增加儿子。

object 和 array 相比, 仅仅多了一个操作 ,即设置 key .

所以我们可以再 object 中设置完 key 之后再调用 给 array 添加儿子的操作来实现给 object 添加儿子。

具体参考胆码。

  1. /* Utility for array list handling. */
  2. static void suffix_object(cJSON *prev,cJSON *item) {
  3. //两个兄弟的指针互相指向对方
  4. prev->next=item;
  5. item->prev=prev;
  6. }
  7.  
  8. /* Add item to array/object. */
  9. void cJSON_AddItemToArray(cJSON *array, cJSON *item) {
  10. cJSON *c=array->child;
  11. if (!item) return;
  12. if (!c) {
  13. array->child=item; //之前没有儿子,直接添加
  14. } else {
  15. while (c && c->next) c=c->next; // 先找到最后一个儿子。
  16. suffix_object(c,item); // 添加儿子, c 是 item 的兄弟
  17. }
  18. }
  19.  
  20. void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {
  21. if (!item) return;
  22. if (item->string) cJSON_free(item->string);//这个 儿子之前有key, 先清理了。
  23. item->string=cJSON_strdup(string); // 设置 key
  24. cJSON_AddItemToArray(object,item); // 添加儿子
  25. }

实际上上面这两个操作即可满足我们的添加需求了。

但是 cjson 为了我们更方便的使用添加节点的操作, 它又封装了一些操作, 当然使用宏定义封装的。

比如我们平常给 object 增加一个 false 儿子需要这样

  1. cJSON_AddItemToObject(object, name, cJSON_CreateFalse())

现在我们只需要这样

  1. cJSON_AddFalseToObject(object,name)

具体实现方式就是定义一个宏。

而且 cjson 只定义了对象的添加,而没有对数组定义这个宏。

大概原因是那时候, 一般一个数组内的元素的类型都是相同的吧, 不像对象这么灵活。

  1. /* Macros for creating things quickly. */
  2. #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
  3. #define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
  4. #define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
  5. #define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
  6. #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
  7. #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))

因此 cjson 还专门为 数组定义了下面的批量创建节点。

  1. /* These utilities create an Array of count items. */
  2. cJSON *cJSON_CreateIntArray(const int *numbers,int count);
  3. cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
  4. cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
  5. cJSON *cJSON_CreateStringArray(const char **strings,int count);

另外, 当我们要添加的节点已经在一个树上的时候, 再向另一个树中添加这个节点时, 这个节点的 pre 和 next 指针会被覆盖。

于是 cjson 又提供了一种引用性添加节点的方法。

简单的说就是在创建一个 item, 新创建的 item 的 value 指针直接指向原来的 value 值, 这样两个 item 就指向了同一个 item 了。

但是这个引用计数是个难题, cjson 也没有处理好, 只能引用一次, 大家可以想象怎么解决。

我们先来看看 cjson 的引用是怎么实现的。

  1. /* Utility for handling references. */
  2. static cJSON *create_reference(cJSON *item) {
  3. cJSON *ref=cJSON_New_Item();
  4. if (!ref) return 0;
  5. memcpy(ref,item,sizeof(cJSON));
  6. ref->string=0;
  7. ref->type|=cJSON_IsReference;
  8. ref->next=ref->prev=0;
  9. return ref;
  10. }

上面的引用计数仅仅存在 type 里面,显示是有问题的。

我们的 value 是保持不变的,所有的引用都指向这个value.

所以我们可以通过一个和 value 类似的东西, 大家都指向这个 东西, 新增加一个引用的时候加1, 释放一个引用的时候减一即可。

这个看着怎么那么像智能指针呢?

这个话题就说到这吧,实现方式很多的,大家自己多想想。

删除儿子节点

删除也是从 array 和 object 中删除,实现就比较简洁了。

  1. void cJSON_DeleteItemFromArray(cJSON *array,int which) {
  2. cJSON_Delete(cJSON_DetachItemFromArray(array,which));
  3. }
  4. void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {
  5. cJSON_Delete(cJSON_DetachItemFromObject(object,string));
  6. }

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. }

查找节点

对于一般类型的item, 我们直接就得到对应的节点.

但是对于 array 和 object , 我们需要查找对应的节点, 所以就需要去查找了。

这个查找算法由 cjson 的储存节点方式决定着。

由于cjson 采用链表储存了, 所以查找当时只能是暴力遍历了。

  1. cJSON *cJSON_GetArrayItem(cJSON *array,int item) {
  2. cJSON *c=array->child;
  3. while (c && item>0) item--,c=c->next;
  4. return c;
  5. }
  6. cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {
  7. cJSON *c=object->child;
  8. while (c && cJSON_strcasecmp(c->string,string)) c=c->next;
  9. return c;
  10. }

修改节点

我们查找到对应的节点了,就可以对节点进行简单的修改了。

什么是简单的修改呢?

节点的类型不是 array 和 object 都可以算是简单类型,可以直接修改修改其值即可。

但是对于 array 和 object, 我们想给他赋值的话,涉及到释放就得内存这个问题。

下面我们来看看 cjson 的实现代码。

  1. /* Replace array/object items with new ones. */
  2. void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {
  3. cJSON *c=array->child;
  4. while (c && which>0) c=c->next,which--;
  5. if (!c) return;
  6. newitem->next=c->next;
  7. newitem->prev=c->prev;
  8. if (newitem->next) newitem->next->prev=newitem;
  9. if (c==array->child) array->child=newitem;
  10. else newitem->prev->next=newitem;
  11. c->next=c->prev=0;
  12. cJSON_Delete(c);
  13. }
  14. void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem) {
  15. int i=0;
  16. cJSON *c=object->child;
  17. while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;
  18. if(c) {
  19. newitem->string=cJSON_strdup(string);
  20. cJSON_ReplaceItemInArray(object,i,newitem);
  21. }
  22. }

看到这,可能会产生一个疑问:为什么不先查找得到那个节点的父节点指向自己的指针的引用呢?

这又是一个很有趣的小知识点, 这里就不展开了。

实际上这是指针的知识点, 经常会在链表中遇到, 一不小心链表就会因为这个小问题而写残了。

我以前曾接介绍过这个问题,但不记得具体在哪里介绍了, 大概实在hash table 研究与实现memcached 源码阅读之 hash table吧。

好了, 这个修改操作其实就是链表的替换操作, 我就不展开讨论这个知识点了。

json 解析

整体解析部分

如果你看过我的sphinx 源码阅读之json, hash table配置分析器的话, 你就会发现这个解析其实就是个自动机。

自动机可以使用一系列状态及模拟栈来实现, 也可以直接使用一些列的递归函数实现。

本质上是等价的, 建议自己都实现一下。

  1. /* Utility to jump whitespace and cr/lf */
  2. static const char *skip(const char *in) {
  3. while (in && *in && (unsigned char)*in<=32) in++;
  4. return in;
  5. }
  6. /* Parse an object - create a new root, and populate. */
  7. cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) {
  8. const char *end=0;
  9. cJSON *c=cJSON_New_Item();
  10. ep=0;
  11. if (!c) return 0; /* memory fail */
  12.  
  13. end=parse_value(c,skip(value));
  14. if (!end) {
  15. cJSON_Delete(c); /* parse failure. ep is set. */
  16. return 0;
  17. }
  18.  
  19. /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
  20. if (require_null_terminated) {
  21. end=skip(end);
  22. if (*end) {
  23. cJSON_Delete(c);
  24. ep=end;
  25. return 0;
  26. }
  27. }
  28. if (return_parse_end) *return_parse_end=end;
  29. return c;
  30. }
  31.  
  32. /* Default options for cJSON_Parse */
  33. cJSON *cJSON_Parse(const char *value) {
  34. return cJSON_ParseWithOpts(value,0,0);
  35. }

上面两个函数, 其实对我们有用的只有一句end=parse_value(c,skip(value));, 也就是我们只需要了解一下parse_value函数即可。

当然,skip 用于用于忽略空白,这里跳过了 ascii 值小于 32 的。

  1. /* Parser core - when encountering text, process appropriately. */
  2. static const char *parse_value(cJSON *item,const char *value) {
  3. if (!value)return 0;/* Fail on null. */
  4. if (!strncmp(value,"null",4)) {
  5. item->type=cJSON_NULL;
  6. return value+4;
  7. }
  8. if (!strncmp(value,"false",5)) {
  9. item->type=cJSON_False;
  10. return value+5;
  11. }
  12. if (!strncmp(value,"true",4)) {
  13. item->type=cJSON_True;
  14. item->valueint=1;
  15. return value+4;
  16. }
  17. if (*value=='"') {
  18. return parse_string(item,value);
  19. }
  20. if (*value=='-' || (*value>='0' && *value<='9')) {
  21. return parse_number(item,value);
  22. }
  23. if (*value=='[') {
  24. return parse_array(item,value);
  25. }
  26. if (*value=='{') {
  27. return parse_object(item,value);
  28. }
  29.  
  30. ep=value;
  31. return 0;/* failure. */
  32. }

parse_value 的实现方式很简单, 根据前几个字符来判断写一个类型是什么。

如果是 null, false 或 true 设置类型,并返回偏移指针。

如果是其他的,则进入对应的函数中。

解析字符串部分

解析字符串时, 对于特殊字符也应该转义,比如 "n" 字符应该转换为 'n' 这个换行符。

当然,如果只有特殊字符转换的话,代码不会又这么长, 对于字符串, 还要支持非 ascii 码的字符, 即 utf8字符。
这些字符在字符串中会编码为 uXXXX 的字符串, 我们现在需要还原为 0-255 的一个字符。

  1. static unsigned parse_hex4(const char *str) {
  2. unsigned h=0;
  3. if (*str>='0' && *str<='9') h+=(*str)-'0';
  4. else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
  5. else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
  6. else return 0;
  7. h=h<<4;
  8. str++;
  9. if (*str>='0' && *str<='9') h+=(*str)-'0';
  10. else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
  11. else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
  12. else return 0;
  13. h=h<<4;
  14. str++;
  15. if (*str>='0' && *str<='9') h+=(*str)-'0';
  16. else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
  17. else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
  18. else return 0;
  19. h=h<<4;
  20. str++;
  21. if (*str>='0' && *str<='9') h+=(*str)-'0';
  22. else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
  23. else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
  24. else return 0;
  25. return h;
  26. }
  27.  
  28. /* Parse the input text into an unescaped cstring, and populate item. */
  29. static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
  30. static const char *parse_string(cJSON *item,const char *str) {
  31. const char *ptr=str+1;
  32. char *ptr2;
  33. char *out;
  34. int len=0;
  35. unsigned uc,uc2;
  36. if (*str!='"') {
  37. ep=str; /* not a string! */
  38. return 0;
  39. }
  40.  
  41. while (*ptr!='"' && *ptr && ++len) if (*ptr++ == '') ptr++;/* Skip escaped quotes. */
  42.  
  43. out=(char*)cJSON_malloc(len+1);/* This is how long we need for the string, roughly. */
  44. if (!out) return 0;
  45.  
  46. ptr=str+1;
  47. ptr2=out;
  48. while (*ptr!='"' && *ptr) {
  49. if (*ptr!='') *ptr2++=*ptr++;
  50. else {
  51. ptr++;
  52. switch (*ptr) {
  53. case 'b':
  54. *ptr2++='b';
  55. break;
  56. case 'f':
  57. *ptr2++='f';
  58. break;
  59. case 'n':
  60. *ptr2++='n';
  61. break;
  62. case 'r':
  63. *ptr2++='r';
  64. break;
  65. case 't':
  66. *ptr2++='t';
  67. break;
  68. case 'u': /* transcode utf16 to utf8. */
  69. uc=parse_hex4(ptr+1);
  70. ptr+=4;/* get the unicode char. */
  71.  
  72. if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0)break;/* check for invalid.*/
  73.  
  74. if (uc>=0xD800 && uc<=0xDBFF) {/* UTF16 surrogate pairs.*/
  75. if (ptr[1]!='' || ptr[2]!='u')break;/* missing second-half of surrogate.*/
  76. uc2=parse_hex4(ptr+3);
  77. ptr+=6;
  78. if (uc2<0xDC00 || uc2>0xDFFF)break;/* invalid second-half of surrogate.*/
  79. uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
  80. }
  81.  
  82. len=4;
  83. if (uc<0x80) len=1;
  84. else if (uc<0x800) len=2;
  85. else if (uc<0x10000) len=3;
  86. ptr2+=len;
  87.  
  88. switch (len) {
  89. case 4:
  90. *--ptr2 =((uc | 0x80) & 0xBF);
  91. uc >>= 6;
  92. case 3:
  93. *--ptr2 =((uc | 0x80) & 0xBF);
  94. uc >>= 6;
  95. case 2:
  96. *--ptr2 =((uc | 0x80) & 0xBF);
  97. uc >>= 6;
  98. case 1:
  99. *--ptr2 =(uc | firstByteMark[len]);
  100. }
  101. ptr2+=len;
  102. break;
  103. default:
  104. *ptr2++=*ptr;
  105. break;
  106. }
  107. ptr++;
  108. }
  109. }
  110. *ptr2=0;
  111. if (*ptr=='"') ptr++;
  112. item->valuestring=out;
  113. item->type=cJSON_String;
  114. return ptr;
  115. }

解析数字

数字解析需要考虑科学计数法, 即大概形式如下图

number-gif

  1. /* Parse the input text to generate a number, and populate the result into item. */
  2. static const char *parse_number(cJSON *item,const char *num) {
  3. double n=0,sign=1,scale=0;
  4. int subscale=0,signsubscale=1;
  5.  
  6. if (*num=='-') sign=-1,num++;/* Has sign? */
  7. if (*num=='0') num++;/* is zero */
  8. if (*num>='1' && *num<='9')don=(n*10.0)+(*num++ -'0');
  9. while (*num>='0' && *num<='9');/* Number? */
  10. if (*num=='.' && num[1]>='0' && num[1]<='9') {
  11. num++; /* Fractional part? */
  12. don=(n*10.0)+(*num++ -'0'),scale--;
  13. while (*num>='0' && *num<='9');
  14. }
  15. if (*num=='e' || *num=='E') {/* Exponent? */
  16. num++;
  17. if (*num=='+') num++;
  18. else if (*num=='-') signsubscale=-1,num++;/* With sign? */
  19. while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');/* Number? */
  20. }
  21.  
  22. n=sign*n*pow(10.0,(scale+subscale*signsubscale));/* number = +/- number.fraction * 10^+/- exponent */
  23.  
  24. item->valuedouble=n;
  25. item->valueint=(int)n;
  26. item->type=cJSON_Number;
  27. return num;
  28. }

解析数组

解析数组, 需要先遇到 '[' 这个符号, 然后挨个的读取节点内容, 节点使用 ',' 分隔, ',' 前后还可能有空格, 最后以 ']' 结尾。

我们要编写的也是这样。

先创建一个数组对象, 判断是否有儿子, 有的话读取第一个儿子, 然后判断是不是有 逗号, 有的话循环读取后面的儿子。

最后读取 ']' 即可。

  1. /* Build an array from input text. */
  2. static const char *parse_array(cJSON *item,const char *value) {
  3. cJSON *child;
  4. if (*value!='[') {
  5. ep=value; /* not an array! */
  6. return 0;
  7. }
  8.  
  9. item->type=cJSON_Array;
  10. value=skip(value+1);
  11. if (*value==']') return value+1;/* empty array. */
  12.  
  13. item->child=child=cJSON_New_Item();
  14. if (!item->child) return 0; /* memory fail */
  15. value=skip(parse_value(child,skip(value)));/* skip any spacing, get the value. */
  16. if (!value) return 0;
  17.  
  18. while (*value==',') {
  19. cJSON *new_item;
  20. if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
  21. child->next=new_item;
  22. new_item->prev=child;
  23. child=new_item;
  24. value=skip(parse_value(child,skip(value+1)));
  25. if (!value) return 0;/* memory fail */
  26. }
  27.  
  28. if (*value==']') return value+1;/* end of array */
  29. ep=value;
  30. return 0;/* malformed. */
  31. }

解析对象

解析对象和解析数组类似, 只不过对象的一个儿子是个 key-value, key 是字符串, value 可能是任何值, key 和 value 用 ":" 分隔。

  1. /* Build an object from the text. */
  2. static const char *parse_object(cJSON *item,const char *value) {
  3. cJSON *child;
  4. if (*value!='{') {
  5. ep=value; /* not an object! */
  6. return 0;
  7. }
  8.  
  9. item->type=cJSON_Object;
  10. value=skip(value+1);
  11. if (*value=='}') return value+1;/* empty array. */
  12.  
  13. item->child=child=cJSON_New_Item();
  14. if (!item->child) return 0;
  15. value=skip(parse_string(child,skip(value)));
  16. if (!value) return 0;
  17. child->string=child->valuestring;
  18. child->valuestring=0;
  19. if (*value!=':') {
  20. ep=value; /* fail! */
  21. return 0;
  22. }
  23. value=skip(parse_value(child,skip(value+1)));/* skip any spacing, get the value. */
  24. if (!value) return 0;
  25.  
  26. while (*value==',') {
  27. cJSON *new_item;
  28. if (!(new_item=cJSON_New_Item()))return 0; /* memory fail */
  29. child->next=new_item;
  30. new_item->prev=child;
  31. child=new_item;
  32. value=skip(parse_string(child,skip(value+1)));
  33. if (!value) return 0;
  34. child->string=child->valuestring;
  35. child->valuestring=0;
  36. if (*value!=':') {
  37. ep=value; /* fail! */
  38. return 0;
  39. }
  40. value=skip(parse_value(child,skip(value+1)));/* skip any spacing, get the value. */
  41. if (!value) return 0;
  42. }
  43.  
  44. if (*value=='}') return value+1;/* end of array */
  45. ep=value;
  46. return 0;/* malformed. */
  47. }

这样都实现后, 字符串解析为 json 对象就实现了。

json 序列化

json 序列化也成为把 json 输出出来。

一般有两种输出:格式化输出,压缩输出。

简单的说就是要不要输出一些空白的问题。

  1. /* Render a cJSON item/entity/structure to text. */
  2. char *cJSON_Print(cJSON *item) {
  3. return print_value(item,0,1);
  4. }
  5. char *cJSON_PrintUnformatted(cJSON *item) {
  6. return print_value(item,0,0);
  7. }
  8.  
  9. /* Render a value to text. */
  10. static char *print_value(cJSON *item,int depth,int fmt) {
  11. char *out=0;
  12. if (!item) return 0;
  13. switch ((item->type)&255) {
  14. case cJSON_NULL:
  15. out=cJSON_strdup("null");
  16. break;
  17. case cJSON_False:
  18. out=cJSON_strdup("false");
  19. break;
  20. case cJSON_True:
  21. out=cJSON_strdup("true");
  22. break;
  23. case cJSON_Number:
  24. out=print_number(item);
  25. break;
  26. case cJSON_String:
  27. out=print_string(item);
  28. break;
  29. case cJSON_Array:
  30. out=print_array(item,depth,fmt);
  31. break;
  32. case cJSON_Object:
  33. out=print_object(item,depth,fmt);
  34. break;
  35. }
  36. return out;
  37. }

由于基本类型输出的实现比较简单,这里就不多说了,这里只说说输出 对象的实现吧。

假设我们要使用格式化输出, 也就是美化输出。

cjson 的做法不是边分析 json 边输出, 而是预先将要输的内容全部按字符串存在内存中, 最后输出整个字符串。

这对于比较大的 json 来说, 内存就是个问题了。

另外,格式化输出依靠的是节点的深度, 这个也可以优化, 一般宽度超过80 时, 就需要从新的一行算起的。

  1. /* Render an object to text. */
  2. static char *print_object(cJSON *item,int depth,int fmt) {
  3. char **entries=0,**names=0;
  4. char *out=0,*ptr,*ret,*str;
  5. int len=7,i=0,j;
  6. cJSON *child=item->child;
  7. int numentries=0,fail=0;
  8. /* Count the number of entries. */
  9. while (child) numentries++,child=child->next;
  10. /* Explicitly handle empty object case */
  11. if (!numentries) {
  12. out=(char*)cJSON_malloc(fmt?depth+4:3);
  13. if (!out)return 0;
  14. ptr=out;
  15. *ptr++='{';
  16. if (fmt) {
  17. *ptr++='n';
  18. for (i=0; i<depth-1; i++) *ptr++='t';
  19. }
  20. *ptr++='}';
  21. *ptr++=0;
  22. return out;
  23. }
  24. /* Allocate space for the names and the objects */
  25. entries=(char**)cJSON_malloc(numentries*sizeof(char*));
  26. if (!entries) return 0;
  27. names=(char**)cJSON_malloc(numentries*sizeof(char*));
  28. if (!names) {
  29. cJSON_free(entries);
  30. return 0;
  31. }
  32. memset(entries,0,sizeof(char*)*numentries);
  33. memset(names,0,sizeof(char*)*numentries);
  34.  
  35. /* Collect all the results into our arrays: */
  36. child=item->child;
  37. depth++;
  38. if (fmt) len+=depth;
  39. while (child) {
  40. names[i]=str=print_string_ptr(child->string);
  41. entries[i++]=ret=print_value(child,depth,fmt);
  42. if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0);
  43. else fail=1;
  44. child=child->next;
  45. }
  46.  
  47. /* Try to allocate the output string */
  48. if (!fail) out=(char*)cJSON_malloc(len);
  49. if (!out) fail=1;
  50.  
  51. /* Handle failure */
  52. if (fail) {
  53. for (i=0; i<numentries; i++) {
  54. if (names[i]) cJSON_free(names[i]);
  55. if (entries[i]) cJSON_free(entries[i]);
  56. }
  57. cJSON_free(names);
  58. cJSON_free(entries);
  59. return 0;
  60. }
  61.  
  62. /* Compose the output: */
  63. *out='{';
  64. ptr=out+1;
  65. if (fmt)*ptr++='n';
  66. *ptr=0;
  67. for (i=0; i<numentries; i++) {
  68. if (fmt) for (j=0; j<depth; j++) *ptr++='t';
  69. strcpy(ptr,names[i]);
  70. ptr+=strlen(names[i]);
  71. *ptr++=':';
  72. if (fmt) *ptr++='t';
  73. strcpy(ptr,entries[i]);
  74. ptr+=strlen(entries[i]);
  75. if (i!=numentries-1) *ptr++=',';
  76. if (fmt) *ptr++='n';
  77. *ptr=0;
  78. cJSON_free(names[i]);
  79. cJSON_free(entries[i]);
  80. }
  81.  
  82. cJSON_free(names);
  83. cJSON_free(entries);
  84. if (fmt) for (i=0; i<depth-1; i++) *ptr++='t';
  85. *ptr++='}';
  86. *ptr++=0;
  87. return out;
  88. }

《完》

无觅相关文章插件,快速提升流量