cJSON开源项目详细解剖1

简介: cJSON开源项目详细解剖

JSON

JSON介绍

百度百科:JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。


JSON语法

JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名。JSON是一个序列化的对象或数组。

数组和对象(大括号包含的)有以key:value格式还可以有value格式的,


如下:

key:value格式

{
  "name"  : "John Doe", 
  "age" :   18, 
  "address": {
        "country"   : "china",
         "zip-code" : "10000"
        }
}


value格式

[3, 1, 4, 1, 5, 9, 2, 6]

混合格式

[1, 2, "3", {
        "a": 4
      }
]


CJSON

源码下载及介绍

顾名思义就是用C语言实现JSON格式的解析器,可以将文本构造成JSON格式数据,也可以将JSON格式数据解析成文本。

下载地址:CJSON源码

下载好之后这是一个zip的压缩包,需要解压。下图就是解压后的所有文件


LICENSE文件和README文件是关于版权的介绍,建议带上就行。

cJSON.ccJSON.h是源码文件,test.c是一个测试文件,tests文件夹里面放一些JSON格式的数据以便测试使用。


CJSON思想

结构体

CJSON是使用双向链表存储数据,访问类型与树结构,所以我们先来了解这个结构体。

typedef struct cJSON
  {
    struct cJSON* next; // 向后链表指针
    struct cJSON* prev; // 向前链表指针
    struct cJSON* child;// 对象或者数组的孩子节点指针
    int type;         // value的类型
    char* valuestring;      // 字符串值
    int valueint;       // 整数值
    double valuedouble;     // 浮点数值 
    char* string;       // 存放key
  } cJSON;


type是值(value)的类型,一共有7种取值:

分别是:False,Ture,NULL,Number,String,Array,Object。


1.Number类型,则valueint或valuedouble中存储着值。

2.int类型,则valueint中存储着值

3.double类型,则valuedouble中存储着值。

4.String类型,则valuestring中存储着值。


画出CJSON结构

现在可以试着将上图的结构画出来。嵌套用child连接,兄弟关系用链表连接。

解析失败的措施

static const char* ep;

将会定位在解析失败的字符上,并将不能解析的字符串输出

函数解析

cJSON_GetErrorPtr

/*
作  用:字符串解析失败函数
返回值:返回全局变量ep
*/
const char* cJSON_GetErrorPtr(void)
{
  return ep;
}

cJSON_strcasecmp

/*
作 用:用来比较参数s1和s2字符串,比较时会自动忽略大小写的差异
参 数:s1、s2是需要比较字符串
返回值:0表示相同  其他数值表示不相同
*/
static int cJSON_strcasecmp(const char* s1, const char* s2)
{
  if (NULL == s1 && NULL == s2)           // s1均为null就返回0
  {
    return 0;
  }
  if (NULL == s1 || NULL == s2)           // 只要有一方为null就返回1
  {
    return 1;
  }
  for (; tolower(*s1) == tolower(*s2); ++s1, ++s2)  // tolower()大写转小写函数
  {
    if (*s1 == '\0')                // 发现全部匹配成功就返回0
    {
      return 0;
    }
  }
  return tolower(*(const unsigned char*)s1) - tolower(*(const unsigned char*)s2);
}

cJSON_strdup

/*
作 用:为str字符串在堆区开辟一块空间
参 数:字符串
返回值:堆区地址
*/
static char* cJSON_strdup(const char* str)
{
  size_t len = 0;                   // size_t等价于unsigned int
  char* copy = NULL;                  // 保存堆区开辟的地址
  len = strlen(str) + 1;                // 计算字符串长度 最后需要加个'\0' 所以长度+1
  copy = (char*)cJSON_malloc(len);          // 为字符串申请空间
  if (NULL == copy)                 // 空间申请失败
  {
    return 0;
  }
  memcpy(copy, str, len);               // 将str复制到copy中,长度为len
  return copy;
}


cJSON_InitHooks

/* 初始化钩子 */
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
  if (NULL == hooks)
  {
    cJSON_malloc = malloc;
    cJSON_free = free;
    return;
  }
  cJSON_malloc = (hooks->malloc_fn) != NULL ? hooks->malloc_fn : malloc;
  cJSON_free = (hooks->free_fn) != NULL ? hooks->free_fn : free;
}


cJSON_New_Item

/*
作 用:为cJSON在堆区申请一块空间
返回值:堆区地址
*/
static cJSON* cJSON_New_Item(void)
{
  cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));  // 申请一个结构体的空间
  if (NULL == node)                 // 申请空间失败
  {
    return NULL;
  }
  memset(node, 0, sizeof(cJSON));           // 初始化堆区开辟的内存
  return node;
}


cJSON_Delete

/*
作 用:删除一个cJSON结构
参 数:结构体地址
*/
void cJSON_Delete(cJSON* c)
{
  cJSON* next = NULL;
  while (NULL != c)
  {
    next = c->next;                 // 指针向后移
    // 当前兄弟节点是最后一个节点且有孩子
    if (0 == (c->type & cJSON_IsReference) && NULL != c->child)
    {
      cJSON_Delete(c->child);
    }
    // 当前兄弟节点是最后一个且没有孩子,若有value为字符串就回收
    if (0 == (c->type & cJSON_IsReference) && NULL != c->valuestring)
    {
      cJSON_free(c->valuestring);
    }
    // 当前兄弟节点是最后一个且没有孩子,若有key也会受
    if (0 == (c->type & cJSON_StringIsConst) && NULL != c->string)
    {
      cJSON_free(c->string);
    }
    cJSON_free(c);                  // 回收根节点
    c = NULL;
  }
}


parse_number

/*
作 用:将输入的字符串解析成数字(正数不需要+号,直接是数字,只有负数才有符号-)
参 数:item-结构体指针   num-字符串指针
返回值:字符串指针
*/
static const char* parse_number(cJSON* item, const char* num)
{ // 
  double n = 0;                   // 保存数字
  double sign = 1;                  // 数字符号的标志  1表示正数  -1表示负数
  double scale = 0;                 // 用于有小数的数字,先不管小数点,记录扩大了多少倍
  int subscale = 0;                 // 指数部分的数值值
  int signsubscale = 1;               // 指数部分的符号位
  if (*num == '-')                  // 负数
  {
    sign = -1;                    // 设置标记
    num++;                      // 指针后移
  }
  if (*num == '0')                  // 是0
  {
    num++;
  }
  /* 整数部分 */
  if (*num >= '1' && *num <= '9')           // 数字不能从0开始
  {
    do
    {
      n = (n * 10.0) + (*num - '0');
      num++;                    // 指针后移
    } while (*num >= '0' && *num <= '9');     // 之后的数字范围就是0-9
  }
  /* 小数部分 */
  if (*num == '.' && num[1] >= '0' && num[1] <= '9')  // 当前字符为"."且下一个字符为数字才有效
  {
    num++;                      // 跳过 小数点
    do
    {
      n = (n * 10.0) + (*num - '0');
      num++;
      scale++;                  // 记录扩大了多少倍
    } while (*num >= '0' && *num <= '9');
  }
  /* 指数部分 */
  if (*num == 'e' || *num == 'E')
  {
    num++;                      // 指针跳过指数
    if (*num == '+')                // 指数位的符号位
    {
      num++;
    }
    else if (*num == '-')
    {
      signsubscale = -1;              // 修改指数位的符号位
      num++;
    }
    while (*num >= '0' && *num <= '9')        // 指数部分的数字位  可以从0开始
    {
      subscale = (subscale * 10) + (*num++ - '0');
    }
  }
  // 将之前扩大的再缩回来 公式:数值符号位 * 数值位 * 10^(指数符号位 * 指数数值位 - 扩大倍数)
  n = sign * n * pow(10.0, (subscale * signsubscale - scale));
  item->valuedouble = n;                // 将该值存入double类型中
  item->valueint = (int)n;              // 将该值存入int类型中
  item->type = cJSON_Number;              // 将type类型设置为数字
  return num;
}


pow2gt

/* 作用:返回比x大的最小的2的N次方数 */
static int pow2gt(int x)
{
  --x;
  x |= x >> 1;
  x |= x >> 2;
  x |= x >> 4;
  x |= x >> 8;
  x |= x >> 16;
  return x + 1;
}


ensure

/*
作 用:判断当前偏移量下比较needed长度的字符是否越界并作出保护措施
参 数:p-输出缓冲结构体指针  needed-比较的长度
返回值:返回比较的首地址
*/
static char* ensure(printbuffer* p, int needed)
{
  char* newbuffer = NULL;
  int newsize = 0;
  if (NULL == p || NULL == p->buffer)      // 结构体为空或者结构体缓冲区为空
  {
    return NULL;
  }
  needed += p->offset;            // 计算原本偏移量 + 新的比较长度
  if (needed <= p->length)          // 判断偏移量+比较长度是否小于字符串长度
  {
    return p->buffer + p->offset;     // 返回初始地址 + 偏移量
  }
  newsize = pow2gt(needed);         // 返回比x大的最小的2的N次方数
  newbuffer = (char*)cJSON_malloc(newsize);
  if (NULL == newbuffer)            // 空间申请失败
  {
    cJSON_free(p->buffer);
    p->length = 0;
    p->buffer = 0;
    return NULL;
  }
  memcpy(newbuffer, p->buffer, p->length);  // 将p->buffer拷贝到newbuffer,长度为参数三
  cJSON_free(p->buffer);            // 回收掉之前用于保存字符串的空间
  p->length = newsize;            // 更新输出缓冲区结构体
  p->buffer = newbuffer;            // 更新输出缓冲区结构体
  return newbuffer + p->offset;
}


update

/*
作 用:获取缓冲区的字符串长度(不是缓冲区大小,缓冲区大于等于字符串长度)
参 数:p-输出缓冲结构体指针
返回值:长度
*/
static int update(printbuffer* p)
{
  char* str = NULL;
  if (NULL == p || NULL == p->buffer)      // 结构体为空或者结构体缓冲区为空
  {
    return 0;
  }
  str = p->buffer + p->offset;  
  return p->offset + strlen(str);        // 偏移量 + 后半段长度
}


print_number

/*
作 用:将给的数字类型结构体转换为字符串存储到p缓冲区中,若p为null就申请空间
参 数:item-给的数字类型结构体  p-缓冲区指针
返回值:未解析的地址
*/
static char* print_number(cJSON* item, printbuffer* p)
{
  char* str = NULL;
  double d = item->valuedouble;
  if (0 == d)
  {
    if (NULL != p)            // 输出缓冲区结构体指针不为空  需要判断添加是否安全 
    {
      str = ensure(p, 2);       // 0转换成字符串 俩个字节  "0\0"
    }
    else
    {
      str = (char*)cJSON_malloc(2); // 为0申请空间
      if (NULL == str)        // 申请失败
      {
        return NULL;
      }
    }
    strcpy(str, "0");         // 拷贝0字符串
  }
  else if (fabs(((double)item->valueint) - d) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) // 判断是整型
  {
    if (NULL != p)
    {
      str = ensure(p, 11);      // 2 ^ 32 + 1(-4294967296,4294967297)可以用11个字符表示。
    }
    else
    {
      str = (char*)cJSON_malloc(11);  // 为2 ^ 32 + 1 申请空间
      if (NULL == str)        // 申请失败
      {
        return NULL;
      }
    }
    sprintf(str, "%d", item->valueint); // 将整型值存储到str指向的缓冲区中
  }
  else  // double类型
  {
    if (p != NULL)            // 2 ^ 64 + 1(18446744073709551617)可以用21个字符表示。
    {
      str = ensure(p, 21);
    }
    else
    {
      str = (char*)cJSON_malloc(21);
      if (NULL == str)
      {
        return NULL;
      }
    }
    if (fabs(floor(d) - d) <= DBL_EPSILON && fabs(d) < 1.0e60)  // 无小数的double类型数字
    {
      sprintf(str, "%.0f", d);
    }
    else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9)       // 范围在(1.0e-6,1.0e9)就用科学计数法
    {
      sprintf(str, "%e", d);
    }
    else                            // 其他情况就用正常写法
    {
      sprintf(str, "%f", d);
    } 
  }
  return str;
}


parse_hex4

/* utf转换函数   */
static unsigned parse_hex4(const char* str)  
{
  unsigned h = 0;
  if (*str >= '0' && *str <= '9')
    h += (*str) - '0';
  else if (*str >= 'A' && *str <= 'F')
    h += 10 + (*str) - 'A';
  else if (*str >= 'a' && *str <= 'f')
    h += 10 + (*str) - 'a';
  else
    return 0;
  h = h << 4;
  str++;
  if (*str >= '0' && *str <= '9')
    h += (*str) - '0';
  else if (*str >= 'A' && *str <= 'F')
    h += 10 + (*str) - 'A';
  else if (*str >= 'a' && *str <= 'f')
    h += 10 + (*str) - 'a';
  else return 0;
  h = h << 4;
  str++;
  if (*str >= '0' && *str <= '9')
    h += (*str) - '0';
  else if (*str >= 'A' && *str <= 'F')
    h += 10 + (*str) - 'A';
  else if (*str >= 'a' && *str <= 'f')
    h += 10 + (*str) - 'a';
  else return 0;
  h = h << 4; str++;
  if (*str >= '0' && *str <= '9')
    h += (*str) - '0';
  else if (*str >= 'A' && *str <= 'F')
    h += 10 + (*str) - 'A';
  else if (*str >= 'a' && *str <= 'f')
    h += 10 + (*str) - 'a';
  else
    return 0;
  return h;
}


parse_string

/*
作 用:将字符串拷贝到堆区
参 数:item-结构体指针   str-字符串指针
返回值:字符串解析后的地址
*/
static const char* parse_string(cJSON* item, const char* str)
{
  const char* ptr = str + 1;      // *str为"字符,所以从下一个开始,遍历字符串的指针
  char* out;              // 用于接受申请在堆区空间的地址
  char* ptr2;             // 指向新开辟的空间的指针 ,用于将这段字符串拷贝在堆区
  int len = 0;            // 计算这段字符串的长度
  unsigned int uc, uc2;
  if (*str != '\"')         // 当*str不为"时发现解析错误
  {
    ep = str;
    return NULL;
  }
  /* 为了计算出这段字符串的长度 */
  while ('\"' != *ptr && '\0' != *ptr)  // 当前字符不能为"和\0
  {
    len++;              
    if (*ptr++ == '\\')         // 跳过转义字符 \  
    {
      ptr++;
    }
  }
  out = (char*)cJSON_malloc(len + 1);   // 给字符串开辟空间 多一个存放\0
  if (NULL == out)            // 空间申请失败
  {
    return NULL;
  }
  ptr = str + 1;              // 指针再回到字符串开始位置
  ptr2 = out;
  while ('\"' != *ptr && '\0' != *ptr)  // 将字符串一个个拷贝到堆区空间
  {
    if ('\\' != *ptr)         // 只要不是转义字符就直接赋值
    {
      *ptr2++ = *ptr++;
    }
    else
    {
      ptr++;              
      switch (*ptr)         // 将转义字符存入堆区那块空间
      {
      case 'b':
        *ptr2++ = '\b';
        break;
      case 'f': 
        *ptr2++ = '\f';
        break;
      case 'n': 
        *ptr2++ = '\n';
        break;
      case 'r': 
        *ptr2++ = '\r';
        break;
      case 't':
        *ptr2++ = '\t';
        break;
      case 'u':  // 将utf16转换为utf8    不做分析
        uc = parse_hex4(ptr + 1);
        ptr += 4; 
        if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0)
          break;  
        if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs. */
        {
          if (ptr[1] != '\\' || ptr[2] != 'u')
            break;  /* missing second-half of surrogate.  */
          uc2 = parse_hex4(ptr + 3);
          ptr += 6;
          if (uc2 < 0xDC00 || uc2>0xDFFF)
            break;  /* invalid second-half of surrogate.  */
          uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF));
        }
        len = 4;
        if (uc < 0x80)
          len = 1;
        else if (uc < 0x800)
          len = 2;
        else if (uc < 0x10000)
          len = 3;
        ptr2 += len;
        switch (len)
        {
        case 4: *--ptr2 = ((uc | 0x80) & 0xBF); uc >>= 6;
        case 3: *--ptr2 = ((uc | 0x80) & 0xBF); uc >>= 6;
        case 2: *--ptr2 = ((uc | 0x80) & 0xBF); uc >>= 6;
        case 1: *--ptr2 = (uc | firstByteMark[len]);
        }
        ptr2 += len;
        break;
      default:  *ptr2++ = *ptr;
        break;
      }                  // 以上是utf16转utf8  不做分析
      ptr++;                 // 指向内存地址的指针后移
    }
  }
  *ptr2 = '\0';               // 拷贝完成最后补'\0'
  if (*ptr == '\"')             // 把字符串最后"跳过
    ptr++;
  item->valuestring = out;          // 将字符串给到结构体中的valuestring
  item->type = cJSON_String;          // 将数据类型设置为字符串
  return ptr;                 // 返回新的字符串地址
}


print_string_ptr

/*
作 用:将字符串转换成文本模式
参 数:str-当前需要转义的字符串地址  p-输出缓冲区结构指针
返回值:返回该字符串在输出缓冲区起始地址
*/
static char* print_string_ptr(const char* str, printbuffer* p)
{
  const char* ptr = NULL;       // 用于遍历查找 和计算字符串长度
  char* ptr2 = NULL;          // 接受out值
  char* out = NULL;         // 获取输出缓冲区的地址
  int len = 0;            // 保存字符串长度
  int flag = 0;           // 1表示字符串包含控制字符或者"或者\  0表示正常字符
  unsigned char token = ' ';
  for (ptr = str; '\0' != *ptr; ptr++)// 判断子串中是否包含控制字符或者"或者\ 有则为1无则为0
  {
    flag |= ((*ptr > 0 && *ptr < 32) || (*ptr == '\"') || (*ptr == '\\')) ? 1 : 0;
    if (1 == flag)
      break;
  }
  if (0 == flag)            // 不包含控制字符 " \ 三种
  {
    len = ptr - str;        // 计算长度
    if (NULL != p)          // 输出缓冲区指针不为NULL
    {
      // 判断当前偏移量下比较len+3(因为需要放俩个""和一个\0)长度的字符是否越界并作出保护措施
      out = ensure(p, len + 3);
    }
    else
    {
      out = (char*)cJSON_malloc(len + 3);// 为字符串申请空间
    }
    if ('\0' == out)        // 空间申请失败
      return NULL;
    ptr2 = out;
    *ptr2++ = '\"';         // 先放第一个"
    strcpy(ptr2, str);        // 再拷贝字符串
    ptr2[len] = '\"';       // 最后放第二个"
    ptr2[len + 1] = '\0';     // 最后补\0
    return out;           // 返回该字符串在输出缓冲区起始地址
  }
  if (NULL == str)          // 字符串为NULL
  {
    if (NULL != p)          // 输出缓冲区指针不为NULL
    {
      // 判断当前偏移量下比较3(因为需要放俩个""和一个\0)长度的字符是否越界并作出保护措施
      out = ensure(p, 3);
    }
    else
    {
      out = (char*)cJSON_malloc(3);// 为字符串申请空间
    }
    if (NULL == out)        // 空间申请失败
      return NULL;
    strcpy(out, "\"\"");      // 想输出缓冲区放入 ""\0
    return out;           // 返回该字符串在输出缓冲区起始地址
  }
  ptr = str;
  while ('\0' != (token = *ptr) && ++len)// 计算字符串总长度
  {
    // 查找字符串 "\bfnrt 中首次出现字符token的位置,
    // 返回首次出现c的位置的指针,如果s中不存在c则返回NULL
    if (NULL != strchr("\"\\\b\f\n\r\t", token))
      len++;
    else if (token < 32)// 控制字符(不可见) 占5字节
      len += 5;
    ptr++;
  }
  if (NULL != p)            // 输出缓冲区指针不为NULL
  {
    // 判断当前偏移量下比较len + 3长度的字符是否越界并作出保护措施
    out = ensure(p, len + 3);
  }
  else
  {
    out = (char*)cJSON_malloc(len + 3); // 为字符串申请空间
  }
  if (NULL == out)          // 空间申请失败
  {
    return NULL;
  }
  ptr2 = out;
  ptr = str;
  *ptr2++ = '\"';           // 存入第一个"
  while ('\0' != *ptr)        // 存入字符串内容
  {
    if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\')// 不是控制字符也不是"和\ 
      *ptr2++ = *ptr++;
    else
    {
      *ptr2++ = '\\';
      switch (token = *ptr++)
      {
      case '\\':  *ptr2++ = '\\';
        break;
      case '\"':  *ptr2++ = '\"';
        break;
      case '\b':  *ptr2++ = 'b';
        break;
      case '\f':  *ptr2++ = 'f';
        break;
      case '\n':  *ptr2++ = 'n';
        break;
      case '\r':  *ptr2++ = 'r';
        break;
      case '\t':  *ptr2++ = 't';
        break;
      default: sprintf(ptr2, "u%04x", token); ptr2 += 5;
        break;  /* escape and print */
      }
    }
  }
  *ptr2++ = '\"';           // 存入第二个"
  *ptr2++ = '\0';           // 存入最后的\0
  return out;             // 返回该字符串在输出缓冲区起始地址
}


print_string

/* 向缓冲区存入字符串*/
static char* print_string(cJSON* item, printbuffer* p)
{
  return print_string_ptr(item->valuestring, p);              
}


skip

/* 跳过控制字符和空格(ASCII-32) */
static const char* skip(const char* in)
{
  while (in != NULL && *in != '\0' && (unsigned char)*in <= 32) // 小于32是控制字符 32是空格
  {
    in++; // 指针后移
  }
  return in;
}


cJSON_ParseWithOpts

/* 检查JSON 是否为空终止,并检索指向解析的最终字节的指针。
  在return_parse_end中提供ptr并且解析失败 */
cJSON* cJSON_ParseWithOpts(const char* value,
  const char** return_parse_end, int require_null_terminated)
{
  const char* end = 0;        // 用于接受跳过空格
  cJSON* c = cJSON_New_Item();    // 申请空间
  ep = '\0';              // 初始化异常指针
  if (NULL == c)            // 申请内存失败
  {
    return NULL;
  }
  end = parse_value(c, skip(value));  // 跳过控制字符和空格,再处理文本
  if (NULL == end)          // 发现没有数据  解析失败
  {
    cJSON_Delete(c);// 释放申请的空间
    return NULL;
  }
  // 如果我们要求没有附加垃圾的空终止JSON,跳过然后检查空终止
  if (require_null_terminated != '\0')
  {
    end = skip(end);        // 跳过空格
    if ('\0' != *end)       // 遇到'\0'
    {
      cJSON_Delete(c);      // 回收
      ep = end;
      return NULL;
    }
  }
  if (return_parse_end != NULL)
  {
    *return_parse_end = end;
  }
  return c;
}


cJSON_Parse

/* cJSON解析默认函数 */
cJSON* cJSON_Parse(const char* value)
{
  return cJSON_ParseWithOpts(value, 0, 0);
}

cJSON_Print

/* 将cJSON格式的结构转成文本格式 */
char* cJSON_Print(cJSON* item)
{
  return print_value(item, 0, 1, NULL);
}

cJSON_PrintUnformatted

/* 将cJSON格式的结构转成文本格式(无格式) */
char* cJSON_PrintUnformatted(cJSON* item)
{
  return print_value(item, 0, 0, 0);
}

cJSON_PrintBuffered

/* 将cJSON格式的结构存储到输出缓冲区 */
char* cJSON_PrintBuffered(cJSON* item, int prebuffer, int fmt)
{
  printbuffer p;
  p.buffer = (char*)cJSON_malloc(prebuffer);
  p.length = prebuffer;
  p.offset = 0;
  return print_value(item, 0, fmt, &p);
  return p.buffer;
}


parse_value

/* 核心解析器——当遇到文本时,处理。 */
static const char* parse_value(cJSON* item, const char* value)
{
  if (value == NULL)// 不能为空。
  {
    return 0;
  }
  // strncmp字符串比较函数,第三个参数为比较长度
  if (strncmp(value, "NULL", 4) == 0)// 判断是否为NULL 然后跳过4个字节
  {
    item->type = cJSON_NULL;
    return value + 4;// 指针后移
  }
  if (strncmp(value, "false", 5) == 0)// 判断是否为false 然后跳过5个字节
  {
    item->type = cJSON_False;
    return value + 5;
  }
  if (strncmp(value, "true", 4) == 0)// 判断是否为true 然后跳过4个字节
  {
    item->type = cJSON_True;
    return value + 4;
  }
  if (*value == '\"')// 判断是否为"
  {
    // 将当前以"开头"结束字符串一段存入结构体,并设置type。返回新的地址
    return parse_string(item, value);
  }
  if (*value == '-' || (*value >= '0' && *value <= '9'))// 这是数字
  {
    return parse_number(item, value);
  }
  if (*value == '[')// 数组
  {
    return parse_array(item, value);
  }
  if (*value == '{')// 大括号
  {
    return parse_object(item, value);
  }
  // 未找到匹配项,将地址给到ep。
  ep = value;
  return 0;
}


print_value

/*
功 能:将json结构体转换为字符串。
参 数:item-json结构体指针   depth-深度  fmt-输出格式  p-输出缓冲区结构体指针
返回值:字符串首地址
*/
static char* print_value(cJSON* item, int depth, int fmt, printbuffer* p)
{
  char* out = NULL;
  if (NULL == item)         // 结构体指针为NULL
  {
    return NULL;
  }
  if (NULL != p)            // 输出缓冲结构指针不为NULL
  {
    switch (item->type)
    {
    case cJSON_NULL:        // 输出缓冲区指针不为NULL且类型为null 直接在原始地址偏移量下拷贝null
    {
      out = ensure(p, 5);     // 检查当前偏移量下还能否放进去5个字符 "null\0" 并返回偏移后的地址
      if (out != NULL)
        strcpy(out, "null");
      break;
    }
    case cJSON_False:       // 输出缓冲区指针不为NULL且类型为false 直接在原始地址偏移量下拷贝false
    {
      out = ensure(p, 6);     // 检查当前偏移量下还能否放进去6个字符 "false\0" 并返回偏移后的地址
      if (out != NULL)
        strcpy(out, "false");
      break;
    }
    case cJSON_True:        // 输出缓冲区指针不为NULL且类型为true 直接在原始地址偏移量下拷贝true
    {
      out = ensure(p, 5);     // 检查当前偏移量下还能否放进去6个字符 "false\0" 并返回偏移后的地址
      if (out != NULL)
        strcpy(out, "true");
      break;
    }
    case cJSON_Number:        // 输出缓冲区指针不为NULL且类型为数字  
      out = print_number(item, p);// 将结构体的数字存入到输出缓冲区中 还会检测能否放下
      break;
    case cJSON_String:        // 输出缓冲区指针不为NULL且类型为数字
      out = print_string(item, p);// 将结构体的字符串存入到输出缓冲区中 还会检测能否放下
      break;
    case cJSON_Array:       // 输出缓冲区指针不为NULL且类型为数字
      out = print_array(item, depth, fmt, p); // 将结构体的数组存入到输出缓冲区中 还会检测能否放下
      break;
    case cJSON_Object:        // 输出缓冲区指针不为NULL且类型为数字
      out = print_object(item, depth, fmt, p);// 将结构体的对象存入到输出缓冲区中 还会检测能否放下
      break;
    }
  }
  else  // 输出缓冲结构指针为NULL  需要申请空间 首次申请不需要检查大小问题
  {
    switch (item->type)
    {
    case cJSON_NULL:  
      out = cJSON_strdup("null");
      break;
    case cJSON_False: 
      out = cJSON_strdup("false");
      break;
    case cJSON_True:  
      out = cJSON_strdup("true");
      break;
    case cJSON_Number:  
      out = print_number(item, NULL);
      break;
    case cJSON_String:  
      out = print_string(item, NULL);
      break;
    case cJSON_Array: 
      out = print_array(item, depth, fmt, NULL);
      break;
    case cJSON_Object:
      out = print_object(item, depth, fmt, NULL);
      break;
    }
  }
  return out;

parse_array

/*
作 用:从输入文本构建数组。
参 数:item-结构体指针   value-字符串指针
返回值:解析后的文本地址
*/
static const char* parse_array(cJSON* item, const char* value)
{
  cJSON* child = NULL;  // 节点指针(结构体指针)
  if (*value != '[')    // 不是数组 匹配失败
  {
    ep = value;
    return 0;
  }
  item->type = cJSON_Array;// 设置类型为数组
  value = skip(value + 1);// 更新指针,跳过一些空格和控制字符
  if (*value == ']')    // 空数组
  {
    return value + 1;
  }
  // 如果不是空数组,就再申请一块空间  这块空间是item的孩子节点
  item->child = child = cJSON_New_Item();
  if (NULL == item->child)// 空间申请失败
  {
    return NULL;
  }
  // 先将指针跳过控制字符和空格,然后将接下来字符串指针找匹配的函数,
  // 然后将其放入孩纸结构体中,最后再将指针跳过控制字符和空格
  value = skip(parse_value(child, skip(value)));
  if (NULL == value)    // 如果字符串指针走完了  就结束
  {
    return NULL;
  }
  while (*value == ',') // 如果碰到逗号
  {
    cJSON* new_item;  // 定义新指针
    new_item = cJSON_New_Item();// 申请新的空间
    if (NULL == new_item)// 空间申请失败
    {
      return NULL;
    }
    // 该节点为孩子节点的弟弟节点  下面是将俩兄弟节点连接起来
    child->next = new_item;
    new_item->prev = child;
    child = new_item; // 更新节点指针
    // 先将指针跳过控制字符和空格,然后将接下来字符串指针找匹配的函数,
    // 然后将其放入孩纸结构体中,最后再将指针跳过控制字符和空格 
    value = skip(parse_value(child, skip(value + 1)));
    if (NULL == value)  // 走完了
    {
      return NULL;
    }
  }
  if (*value == ']')    // 数组结束标志
  {
    return value + 1;
  }
  ep = value;       // 特殊情况,为找打数组结束标志
  return NULL;
}


目录
相关文章
|
1月前
|
存储 Rust Go
Python 潮流周刊#16:优雅重要么?如何写出 Pythonic 的代码?
Python 潮流周刊#16:优雅重要么?如何写出 Pythonic 的代码?
35 0
|
1月前
|
存储 JSON NoSQL
cJSON项目解析
cJSON项目解析
cJSON开源项目详细解剖2
cJSON开源项目详细解剖2
48 0
cJSON开源项目详细解剖3
cJSON开源项目详细解剖3
136 0
|
机器学习/深度学习 SQL 存储
Python数据分析库介绍及引入惯例
Python数据分析库介绍及引入惯例
118 0
造孽啊!阿里内部的神级项目和JDK源码阅读指南竟惨遭GitHub开源
背景 今天逛GitHub,发现了一个神级项目,作者将整理/记录阅读JDK源码时的理解与体会分享出来,便于大家学习。这里将项目分享出来,让小伙伴们能更好地学习Java。
|
Rust IDE 前端开发
GitHub 官宣“报废”Atom 编辑器,创始团队不甘心表示正用 Rust 重写
GitHub 有 Atom,微软有 Visual Studio Code。但向来是一山不容二虎。
685 0
GitHub 官宣“报废”Atom 编辑器,创始团队不甘心表示正用 Rust 重写
|
自然语言处理 算法 编译器
方舟编译器最新技术细节
方舟编译器最新技术细节
方舟编译器最新技术细节