stucky 发表于 2016-5-6 21:28:05

cjson源码阅读

本帖最后由 stucky 于 2016-5-6 22:36 编辑

基本信息
在阅读源码之前可以先看看json的介绍 中文介绍

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

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


来了解下cjson的结构体定义 和类型的定义

//定义cjson值的类型
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512

//json为一项"键-值"的数据
typedef struct cJSON {
      //同一级的cjson结构 利用双向链表存储
      struct cJSON *next, *prev;      

      //如果是object 或 array的话 则为第一个儿子的指针
//也就是object 或 array中 可以嵌套object 或 array
      struct cJSON *child;

      //本项的类型
      int type;
      //value
      char *valuestring;
      int valueint;
      double valuedouble;
      //key
      char *string;
} cJSON;


内存管理

typedef struct cJSON_Hooks {
      void *(*malloc_fn)(size_t sz);
      void(*free_fn)(void *ptr);
} cJSON_Hooks;

void cJSON_InitHooks(cJSON_Hooks* hooks)
{
      if (!hooks) { /* Reset hooks */
                cJSON_malloc = malloc;
                cJSON_free = free;
                return;
      }
      cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc;
      cJSON_free = (hooks->free_fn) ? hooks->free_fn : free;
}

cjson使用的内存管理为c语言中常用的malloc和free
这里使用了hook技术使得用户可以自定义内存管理

创建节点

//返回一个cjson结构指针
static cJSON *cJSON_New_Item(void)
{
      cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
      if (node) memset(node, 0, sizeof(cJSON));
      return node;
}


根据对应的类型来生成对应类型的节点

//创建基础类型
cJSON *cJSON_CreateNull(void)
{
      cJSON *item = cJSON_New_Item();
      if (item)item->type = cJSON_NULL;
      return item;
}

cJSON *cJSON_CreateTrue(void)
{
      cJSON *item = cJSON_New_Item();
      if (item)item->type = cJSON_True;
      return item;
}

cJSON *cJSON_CreateFalse(void)
{
      cJSON *item = cJSON_New_Item();
      if (item)item->type = cJSON_False;
      return item;
}

cJSON *cJSON_CreateBool(int b)
{
      cJSON *item = cJSON_New_Item();
      if (item)item->type = b ? cJSON_True : cJSON_False;
      return item;
}

cJSON *cJSON_CreateNumber(double num)
{
      cJSON *item = cJSON_New_Item();
      if (item)
      {
                item->type = cJSON_Number;
                item->valuedouble = num;
                item->valueint = (int)num;
      }
      return item;
}

cJSON *cJSON_CreateString(const char *string)
{
      cJSON *item = cJSON_New_Item();
      if (item)
      {
                item->type = cJSON_String;
                item->valuestring = cJSON_strdup(string);
      }
      return item;
}
cJSON *cJSON_CreateArray(void)
{
      cJSON *item = cJSON_New_Item();
      if (item)item->type = cJSON_Array;
      return item;
}

cJSON *cJSON_CreateObject(void)
{
      cJSON *item = cJSON_New_Item();
      if (item)item->type = cJSON_Object;
      return item;
}

//创建数组
cJSON *cJSON_CreateIntArray(const int *numbers, int count)               
{
      int i;
      cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
      for (i = 0; a && i < count; i++)
      {
                n = cJSON_CreateNumber(numbers);
                if (!i)
                        a->child = n;
                else suffix_object(p, n);
                p = n;
      }
      return a;
}

cJSON *cJSON_CreateFloatArray(const float *numbers, int count)      
{
      int i;
      cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
      for (i = 0; a && i < count; i++)
      {
                n = cJSON_CreateNumber(numbers);
                if (!i)
                        a->child = n;
                else
                        suffix_object(p, n);
                p = n;
      }
      return a;
}

cJSON *cJSON_CreateDoubleArray(const double *numbers, int count)      
{
      int i;
      cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
      for (i = 0; a && i < count; i++)
      {
                n = cJSON_CreateNumber(numbers);
                if (!i)
                        a->child = n;
                else
                        suffix_object(p, n);
                p = n;
      }
      return a;
}

cJSON *cJSON_CreateStringArray(const char **strings, int count)      
{
      int i;
      cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
      for (i = 0; a && i < count; i++)
      {
                n = cJSON_CreateString(strings);
                if (!i)
                        a->child = n;
                else suffix_object(p, n);
                p = n;
      }
      return a;
}


在处理字符串的时候会用到一个cJSON_strdup函数 实则为申请空间,将字符串复制到此处空间中

//申请空间并复制字符串
static char* cJSON_strdup(const char* str)
{
      size_t len;
      char* copy;

      len = strlen(str) + 1;
      if (!(copy = (char*)cJSON_malloc(len))) return 0;
      memcpy(copy, str, len);
      return copy;
}


删除节点

删除节点很简单 删除儿子
如果是字符串类型 删除字符串的value
最后删除key
最后将自己删除


//释放json结构体
void cJSON_Delete(cJSON *c)
{
      cJSON *next;
      while (c)
      {
                next = c->next;
                if (!(c->type&cJSON_IsReference) && c->child)
                        cJSON_Delete(c->child);
                if (!(c->type&cJSON_IsReference) && c->valuestring)
                        cJSON_free(c->valuestring);
                if (!(c->type&cJSON_StringIsConst) && c->string)
                        cJSON_free(c->string);
                cJSON_free(c);
                c = next;
      }
}


节点管理

json可以理解为一棵树
节点操作就是把节点b 添加为节点a的儿子 或者从节点a中把节点b删除
修改节点a 查询节点a
这就是数据结构中的增删改查

添加儿子节点
添加儿子节点有两种操作 一种是给object添加儿子 一种是给array添加儿子


//对双向链表进行处理
static void suffix_object(cJSON *prev, cJSON *item)
{
      prev->next = item;
      item->prev = prev;
}

//增加项到数组或对象中
void cJSON_AddItemToArray(cJSON *array, cJSON *item)
{
      cJSON *c = array->child;
      if (!item)
                return;
      //如果为空则为头指针
      if (!c)
      {
                array->child = item;
      }
      else
      {
                while (c && c->next)
                        c = c->next;
                suffix_object(c, item);
      }
}

void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
{
      if (!item)
                return;
      if (item->string)
                cJSON_free(item->string);
      item->string = cJSON_strdup(string);
      cJSON_AddItemToArray(object, item);
}

void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
{
      if (!item)
                return;
      if (!(item->type&cJSON_StringIsConst) && item->string)
                cJSON_free(item->string);
      item->string = (char*)string;
      item->type |= cJSON_StringIsConst;
      cJSON_AddItemToArray(object, item);
}


cjson里面定义了一些宏函数来方便我们快速添加各种类型到object里面


//各种类型添加到object
#define cJSON_AddNullToObject(object,name)                cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name)                cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name)                cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b)      cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n)      cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s)      cJSON_AddItemToObject(object, name, cJSON_CreateString(s))

当一个节点在一个树上的时候,将这个节点插入到另外一个树上的时候,这个节点的prev和next将会被覆盖

cjson提供了一种引用添加节点的方法


void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
{
      cJSON_AddItemToArray(array, create_reference(item));
}

void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
{
      cJSON_AddItemToObject(object, string, create_reference(item));
}


也就是新创建一个item,新创建的item的value指针和原来的value值一样 这样两个item指向同一个item

删除儿子节点


cJSON *cJSON_DetachItemFromArray(cJSON *array, int which)                        
{
      cJSON *c = array->child;
      while (c && which > 0)
                c = c->next, which--;
      if (!c)
                return 0;
      if (c->prev)
                c->prev->next = c->next;
      if (c->next)
                c->next->prev = c->prev;
      if (c == array->child)
                array->child = c->next;
      c->prev = c->next = 0;
      return c;
}

void cJSON_DeleteItemFromArray(cJSON *array, int which)                        
{
      cJSON_Delete(cJSON_DetachItemFromArray(array, which));
}

cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string)
{
      int i = 0;
      cJSON *c = object->child;
      while (c && cJSON_strcasecmp(c->string, string))
                i++, c = c->next;
      if (c)
                return cJSON_DetachItemFromArray(object, i);
      return 0;
}

void cJSON_DeleteItemFromObject(cJSON *object, const char *string)
{
      cJSON_Delete(cJSON_DetachItemFromObject(object, string));
}


删除很简单 从object和array中删除即可

但是cjson 提供了一个Detach 也就是把要删除的节点 从树中分离出来 但是不进行内存释放

这样我们可以做很多事,比如添加到另外一个json中

查找节点

//获取数组中的某项
cJSON *cJSON_GetArrayItem(cJSON *array, int item)                              
{
      cJSON *c = array->child;
      while (c && item > 0)
                item--, c = c->next;
      return c;
}

//根据键值获取项
cJSON *cJSON_GetObjectItem(cJSON *object, const char *string)      
{
      cJSON *c = object->child;
      while (c && cJSON_strcasecmp(c->string, string))
                c = c->next;
      return c;
}


因为是链表,所以搜索为暴力搜索

修改节点


void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)                {
      cJSON *c = array->child;
      while (c && which > 0)
                c = c->next, which--;
      if (!c)
                return;
      newitem->next = c->next;
      newitem->prev = c->prev;
      if (newitem->next)
                newitem->next->prev = newitem;
      if (c == array->child)
                array->child = newitem;
      else
                newitem->prev->next = newitem;
      c->next = c->prev = 0;
      cJSON_Delete(c);
}

void cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
{
      int i = 0;
      cJSON *c = object->child;
      while (c && cJSON_strcasecmp(c->string, string))
                i++, c = c->next;
      if (c)
      {
                newitem->string = cJSON_strdup(string);
                cJSON_ReplaceItemInArray(object, i, newitem);
      }
}


修改操作很简单,对链表的简单操作

插入操作


void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)                {
      cJSON *c = array->child;
      while (c && which > 0)
                c = c->next, which--; if (!c)
      {
                cJSON_AddItemToArray(array, newitem);
                return;
      }
      newitem->next = c;
      newitem->prev = c->prev;
      c->prev = newitem;
      if (c == array->child)
                array->child = newitem;
      else
                newitem->prev->next = newitem;
}


插入操作也是简单的链表操作

json解析
整体解析

//跳过小于等于32的ansi码 也就是跳过空白
static const char *skip(const char *in)
{
      while (in && *in && (unsigned char)*in <= 32)
                in++;
      return in;
}

//创建一个对象 创建一个新的头 并填充
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 (!c)
                return 0;
      //分析值
      end = parse_value(c, skip(value));
      //如果分析失败 释放内存 设置ep
      if (!end)      
      {
                cJSON_Delete(c);
                return 0;
      }
      //处理附带垃圾的字符串
      if (require_null_terminated)
      {
                end = skip(end);
                if (*end)
                {
                        cJSON_Delete(c);
                        ep = end;
                        return 0;
                }
      }
      //是否返回处理后的字符串
      if (return_parse_end)
                *return_parse_end = end;
      return c;
}


上面两个函数 我们需要了解一下parse_value 和 skip

skip很简单 其实就是跳过字符串中的空白

parse_value就是根据前几个字符来判断是什么类型


static const char *parse_value(cJSON *item, const char *value)
{
      if (!value)                                                return 0;      /* Fail on null. */
      if (!strncmp(value, "null", 4))      { item->type = cJSON_NULL;return value + 4; }
      if (!strncmp(value, "false", 5))      { item->type = cJSON_False; return value + 5; }
      if (!strncmp(value, "true", 4))      { item->type = cJSON_True; item->valueint = 1;      return value + 4; }
      if (*value == '\"')                              { 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 = value; return 0;      /* failure. */
}


解析字符串部分

解析字符串时 需要对转义字符和utf8字符进行处理


static const char *parse_string(cJSON *item, const char *str)
{
      const char *ptr = str + 1; char *ptr2; char *out; int len = 0; unsigned uc, uc2;
      if (*str != '\"') { ep = str; return 0; }      /* not a string! */

      //跳过引号和转移符号并得到字符串的长度
      while (*ptr != '\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++;      /* Skip escaped quotes. */

      //申请一块大小等于字符串长度的内存
      out = (char*)cJSON_malloc(len + 1);
      if (!out) return 0;

      ptr = str + 1; ptr2 = out;
      while (*ptr != '\"' && *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':         /* transcode utf16 to utf8. */
                              uc = parse_hex4(ptr + 1); ptr += 4;      /* get the unicode char. */

                              if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0)      break;      /* check for invalid.      */

                              if (uc >= 0xD800 && uc <= 0xDBFF)      /* UTF16 surrogate pairs.      */
                              {
                                        if (ptr != '\\' || ptr != '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);
                              }
                              ptr2 += len;
                              break;
                        default:*ptr2++ = *ptr; break;
                        }
                        ptr++;
                }
      }
      *ptr2 = 0;
      if (*ptr == '\"') ptr++;
      //设置项的类型和值
      item->valuestring = out;
      item->type = cJSON_String;
      //处理完后返回
      return ptr;
}

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


解析数字部分




static const char *parse_number(cJSON *item, const char *num)
{
      double n = 0, sign = 1, scale = 0; int subscale = 0, signsubscale = 1;

      if (*num == '-') sign = -1, num++;      /* Has sign? */
      if (*num == '0') num++;                        /* is zero */
      if (*num >= '1' && *num <= '9')      do      n = (n*10.0) + (*num++ - '0');      while (*num >= '0' && *num <= '9');      /* Number? */
      if (*num == '.' && num >= '0' && num <= '9') { num++;                do      n = (n*10.0) + (*num++ - '0'), scale--; while (*num >= '0' && *num <= '9'); }      /* Fractional part? */
      if (*num == 'e' || *num == 'E')                /* Exponent? */
      {
                num++; if (*num == '+') num++;      else if (*num == '-') signsubscale = -1, num++;                /* With sign? */
                while (*num >= '0' && *num <= '9') subscale = (subscale * 10) + (*num++ - '0');      /* Number? */
      }

      n = sign*n*pow(10.0, (scale + subscale*signsubscale));      /* number = +/- number.fraction * 10^+/- exponent */

      item->valuedouble = n;
      item->valueint = (int)n;
      item->type = cJSON_Number;
      return num;
}


解析数组

解析数组的时候先需要遇到'[' 然后挨个读取节点的内容 节点用,进行分割,前后还可以能有空白需要跳过 最后以']'结尾


static const char *parse_array(cJSON *item, const char *value)
{
      cJSON *child;
      if (*value != '[')      { ep = value; return 0; }      /* not an array! */

      item->type = cJSON_Array;
      value = skip(value + 1);
      if (*value == ']') return value + 1;      /* empty array. */

      item->child = child = cJSON_New_Item();
      if (!item->child) return 0;               /* memory fail */
      value = skip(parse_value(child, skip(value)));      /* skip any spacing, get the value. */
      if (!value) return 0;

      while (*value == ',')
      {
                cJSON *new_item;
                if (!(new_item = cJSON_New_Item())) return 0;         /* memory fail */
                child->next = new_item; new_item->prev = child; child = new_item;
                value = skip(parse_value(child, skip(value + 1)));
                if (!value) return 0;      /* memory fail */
      }

      if (*value == ']') return value + 1;      /* end of array */
      ep = value; return 0;      /* malformed. */
}


解析对象

解析对象和解析数组类似 对象的儿子里面有key-value key是字符串 value可以是任何值 key和value用":"分隔


static const char *parse_object(cJSON *item, const char *value)
{
      cJSON *child;
      if (*value != '{')      { ep = value; return 0; }      /* not an object! */

      item->type = cJSON_Object;
      value = skip(value + 1);
      if (*value == '}') return value + 1;      /* empty array. */

      //创建新的项
      item->child = child = cJSON_New_Item();
      if (!item->child) return 0;

      //为新的项建立对应的键名
      value = skip(parse_string(child, skip(value)));
      if (!value) return 0;
      child->string = child->valuestring;
      child->valuestring = 0;
      if (*value != ':') { ep = value; return 0; }      /* fail! */
      //为新的项建立对应的值
      value = skip(parse_value(child, skip(value + 1)));      /* skip any spacing, get the value. */
      if (!value) return 0;

      while (*value == ',')
      {
                cJSON *new_item;
                if (!(new_item = cJSON_New_Item()))      return 0; /* memory fail */

                //连接object
                child->next = new_item;
                new_item->prev = child;
                child = new_item;

                value = skip(parse_string(child, skip(value + 1)));
                if (!value) return 0;
                child->string = child->valuestring; child->valuestring = 0;
                if (*value != ':') { ep = value; return 0; }      /* fail! */
                value = skip(parse_value(child, skip(value + 1)));      /* skip any spacing, get the value. */
                if (!value) return 0;
      }

      if (*value == '}') return value + 1;      /* end of array */
      ep = value; return 0;      /* malformed. */
}

json的输出

json的输出 有格式化输出和非格式化输出
这里就不贴源码出来了,有需要的自己可以看看

最重要的是要不要输出一些空白的问题。

到此 整个json阅读完毕




wuhuei153 发表于 2016-5-12 07:01:15

这是啥呀 ,没搞懂
页: [1]
查看完整版本: cjson源码阅读