C json实战引擎 二 , 实现构造部分

引言

  这篇博文和前一篇 C json实战引擎一,实现解析部分设计是相同的,都是采用递归下降分析.

这里扯一点 假如你是学生 推荐一本书 给 大家

  自制编程语言 http://baike.baidu.com/link?url=jIFOBNt26ykhnPr2-UaaDc5_I7gZURXdJ15P2iBXwbglXkdz2qT_tqAz4KoFF0rsS2IQbIP-ij2Ar5EMRzMcuq

当然学了上面内容,以后对编译链接设计方面会有很大提高. 但是对于 其它 也没有什么鸟用.

再扯一点 如果想流畅的看完并成功写完上面书中三个案例. 你还需要 看完 市面上关于 C 讲解的所有出名的 有意义的书籍.

编程很简单,勤能补拙, 想提高就会提高. 但成长很难......

  风暴 http://music.163.com/#/song?id=211222  陈洁仪

 

前言

  同样,一开始将最有益,最简单的部分代码拿出来供大家分享.

1.从简单 有益代码来

  开始分析一段 代码, 先展示一下使用的数据结构

struct cjson {
	struct cjson *next, *prev;
	struct cjson *child; // type == _CJSON_ARRAY or type == _CJSON_OBJECT 那么 child 就不为空

	int type;
	char *key;	// json内容那块的 key名称 	
	char *vs;	// type == _CJSON_STRING, 是一个字符串 	
	double vd;  // type == _CJSON_NUMBER, 是一个num值, ((int)c->vd) 转成int 或 bool
};

//定义cjson_t json类型
typedef struct cjson* cjson_t;

 再展示用的 tstring 结构

#ifndef _STRUCT_TSTRING
#define _STRUCT_TSTRING
//简单字符串结构,并定义文本字符串类型tstring
struct tstring {
    char* str;        //字符串实际保存的内容
    int len;        //当前字符串大小
    int size;        //字符池大小
};
typedef struct  tstring* tstring;
#endif // !_STRUCT_TSTRING

 这些数据结构前面 博文已经对其进行过详细 设计利用分析.

先看一个 double 变成 cjson_t 的算法, 很多细节真的适合 学习尝试用于底层库封装设计中.

// 将item 中值转换成字符串 保存到p中
static char* __print_number(cjson_t item, tstring p)
{
	char* str = NULL;
	double d = item->vd;
	int i = (int)d;
	
	if (d == 0) {  //普通0
		str = __ensure(p, 2);
		if (str)
			str[0] = '0', str[1] = '\0';
	}
	else if ((fabs(d - i)) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) {
		str = __ensure(p, 21); //int 值 
		if (str)
			sprintf(str, "%d", i);
	}
	else {
		str = __ensure(p, 64); //double值 
		if (str) {
			double nd = fabs(d); //得到正值开始比较
			if(fabs(floor(d) - d) <= DBL_EPSILON && nd < 1.0e60)
				sprintf(str, "%.0f", d);
			else if(nd < 1.0e-6 || nd > 1.0e9) //科学计数法
				sprintf(str, "%e", d);
			else
				sprintf(str, "%f", d);

		}
	}

	return str;
}

 是不是感觉 很巧妙. 这里 把 int 和 double 都算作 number类型, 出现了 上面算法. 需要导入 #include <float.h> 引用了 DBL_EPSILON 判断是否相等宏阀值.

 其中 __ensure 函数 是一个 协助 tstring 分配内存的一个函数

/*
 *     这里使用 tstring 结构 size 这里表示 字符串总大小,没有变化
 * len 表示当前字符串的字符串起始偏移量 即 tstring->str + tstring->len 起始的
 */
static char* __ensure(tstring p, int need)
{
    char* nbuf;
    int nsize;
    if (!p || !p->str) {
        SL_FATAL("p:%p need:%p is error!", p, need);
        return NULL;
    }
    need += p->len;
    if (need <= p->size) //内存够用直接返回结果
        return p->str + p->len;
    nsize = __pow2gt(need);
    if ((nbuf = malloc(nsize*sizeof(char))) == NULL) {
        free(p->str);
        p->size = p->len = 0;
        p->str = NULL;
        SL_FATAL("malloc nsize = %d error!", nsize);
        return NULL;
    }
    //这里复制内容
    memcpy(nbuf, p->str, p->size);
    free(p->str);
    p->size = nsize;
    p->str = nbuf;
    return nbuf + p->len;
}

 这里采用的 SL_FATAL 日志库, 看我前面博文 如何写一个 高效多用户的 日志库, 特别有用,基本上是开发中标配.

还有一个 __pow2gt(x) 函数技巧, 返回 一个比x 的 n 其中n是2的幂,并且是最小的幂.是一种技巧记住就可以了.估计都是那些写汇编的老代码遗留下来的潜规则吧.

性能没的说. 不明白就当有个印象.

 1 // 2^n>=x , n是最小的整数
 2 static int __pow2gt(int x)
 3 {
 4     --x;
 5     x |= x >> 1;
 6     x |= x >> 2;
 7     x |= x >> 4;
 8     x |= x >> 8;
 9     x |= x >> 16;
10     return x + 1;
11 }

到这里 这几个函数 就可以代表这整篇文章了. 后面就可以省略了.

 

正文

1.开始说 cjson 的 构造

  cjson 解析 先认为 所有的都是 一个 value => null or bool or number or string or array or object

其中 array or object 需要再特殊处理,因为其中可能包含 value 即 array or object => value

这样的递归顺序进行的. 这就是传说中的低估下降分析 !!!! 爽不爽 , 当我还是学生的时候,NB任务告诉我学会了 递归了下降分析就可以找个

不错的工作, 找个不错的对象. 现在只想说 呵呵!!.

  大概像下面调用关系图

递归嵌套. 好像 Linux 之父 也 说过 去它码的递归.

 

2.展示 cjson 构造用的接口

  这里比较简单,今天只分析 构造部分 接口就一个

// --------------------------------- 下面是 cjson 输出部分的处理代码 -----------------------------------------

/*
 *  这里是将 cjson_t item 转换成字符串内容,需要自己free 
 * item        : cjson的具体结点
 *            : 返回生成的item的json串内容
 */
extern char* cjson_print(cjson_t item);

值得注意的是 上面接口能够将 item变成 char*, 这个char*是堆上分配的. 需要自己 用完后 free.

  

3. 展示 cjson 部分代码

  上面接口构造的函数为

#define _INT_CJONSTR    (256)
/*
*  这里是将 cjson_t item 转换成字符串内容,需要自己free
* item        : cjson的具体结点
*            : 返回生成的item的json串内容
*/
char* 
cjson_print(cjson_t item)
{
    struct tstring p;
    char* out;
    if ((!item) || !(p.str = malloc(sizeof(char)*_INT_CJONSTR))) {
        SL_FATAL("item:%p, p.str = malloc is error!", item);
        return NULL;
    }
    p.size = _INT_CJONSTR;
    p.len = 0;

    out = __print_value(item, &p); //从值处理开始, 返回最终结果
    if (out == NULL) {
        free(p.str);
        SL_FATAL("__print_value item:%p, p:%p is error!", item, &p);
        return NULL;
    }
    return realloc(out,strlen(out) + 1); // 体积变小 realloc返回一定成功
}

核心 是 __print_value 当然设计方面也参照了一点 cJSON内容.  那我们 继续细说 它

//这里是 递归下降 的函数声明处, 分别是处理值, 数组, object
static char* __print_value(cjson_t item, tstring p);
static char* __print_array(cjson_t item, tstring p);
static char* __print_object(cjson_t item, tstring p);

// 定义实现部分, 内部私有函数 认为 item 和 p都是存在的
static char* __print_value(cjson_t item, tstring p) 
{
    char* out = NULL;
    switch ((item->type) & UCHAR_MAX) { // 0xff
    case _CJSON_FALSE: if ((out = __ensure(p, 6))) strcpy(out, "false"); break;
    case _CJSON_TRUE: if ((out = __ensure(p, 5))) strcpy(out, "true"); break;
    case _CJSON_NULL: if ((out = __ensure(p, 5))) strcpy(out, "null"); break;
    case _CJSON_NUMBER:    out = __print_number(item, p); break;
    case _CJSON_STRING:    out = __print_string(item->vs, p); break;
    case _CJSON_ARRAY:    out = __print_array(item, p); break;
    case _CJSON_OBJECT:    out = __print_object(item, p); break;
    }

    return out;
}

有没有感觉 很自然就是这样的.  上面先声明的 __print_* 系列函数,是为了告诉编译器这个函数地址是什么,方便它能找到 并进入处理.

再展示 其中 __print_object 处理函数, 也很直白

 1 // 同样 假定 item 和 p都是存在且不为NULL, 相信这些代码是安全的
 2 static char* __print_object(cjson_t item, tstring p)
 3 {
 4     char* ptr;
 5     int i, ncut, len;
 6     cjson_t child = item->child;
 7 
 8     // 得到孩子结点的深度
 9     for (ncut = 0; child; child = child->child)
10         ++ncut;
11     if (!ncut) {
12         char* out = NULL;
13         if (!(out = __ensure(p, 3)))
14             strcpy(out, "{}");
15         return out;
16     }
17 
18     i = p->len;
19     if (!(ptr = __ensure(p, 2)))
20         return NULL;
21     *ptr++ = '{';
22     *ptr -= '\0';
23     p->len += 1;
24     // 根据子结点 处理
25     for (child = item->child; (child); child = child->next) {
26         __print_string(child->key, p);
27         p->len = __update(p);
28 
29         //加入一个冒号
30         if (!(ptr = __ensure(p, 1)))
31             return NULL;
32         *ptr++ = ':';
33         p->len += 1;
34 
35         //继续打印一个值
36         __print_value(child, p);
37         p->len = __update(p);
38 
39         //结算最后内容
40         len = child->next ? 1 : 0;
41         if ((ptr = __ensure(p, len + 1)) == NULL)
42             return NULL;
43         if (child->next)
44             *ptr++ = ',';
45         *ptr = '\0';
46         p->len += len;
47     }
48     if (!(ptr = __ensure(p, 2)))
49         return NULL;
50     *ptr++ = '}';
51     *ptr = '\0';
52     return p->str + i;
53 }

先处理key ,后面value 用 __print_value 处理. 到这里 基本思路都有了,其它是靠你自己努力临摹 把键盘敲烂!

完整部分代码如下

 cjson.h / 有些辅助接口没有实现,下一个博文中全部实现

  1 #ifndef _H_CJSON
  2 #define _H_CJSON
  3 
  4 // json 中几种数据类型定义 , 对于C而言 最难的是看不见源码,而不是api复杂, 更不是业务复杂
  5 #define _CJSON_FALSE    (0)
  6 #define _CJSON_TRUE        (1)
  7 #define _CJSON_NULL        (2)
  8 #define _CJSON_NUMBER    (3)
  9 #define _CJSON_STRING    (4)
 10 #define _CJSON_ARRAY    (5)
 11 #define _CJSON_OBJECT    (6)
 12 
 13 #define _CJSON_ISREF    (256)        //set 时候用如果是引用就不释放了
 14 #define _CJSON_ISCONST    (512)        //set时候用, 如果是const char* 就不释放了
 15 
 16 struct cjson {
 17     struct cjson *next, *prev;
 18     struct cjson *child; // type == _CJSON_ARRAY or type == _CJSON_OBJECT 那么 child 就不为空
 19 
 20     int type;
 21     char *key;    // json内容那块的 key名称     
 22     char *vs;    // type == _CJSON_STRING, 是一个字符串     
 23     double vd;  // type == _CJSON_NUMBER, 是一个num值, ((int)c->vd) 转成int 或 bool
 24 };
 25 
 26 //定义cjson_t json类型
 27 typedef struct cjson* cjson_t;
 28 
 29 /*
 30  * 这个宏,协助我们得到 int 值 或 bool 值 
 31  * 
 32  * item : 待处理的目标cjson_t结点
 33  */
 34 #define cjson_getint(item) \
 35     ((int)((item)->vd))
 36 
 37 /*
 38  *  删除json串内容  
 39  * c        : 待释放json_t串内容
 40  */
 41 extern void cjson_delete(cjson_t* pc);
 42 
 43 /*
 44  * 对json字符串解析返回解析后的结果
 45  * jstr        : 待解析的字符串
 46  */
 47 extern cjson_t cjson_parse(const char* jstr);
 48 
 49 /*
 50  * 根据 item当前结点的 next 一直寻找到 NULL, 返回个数
 51  *推荐是数组使用
 52  * array    : 待处理的cjson_t数组对象
 53  *            : 返回这个数组中长度
 54  */
 55 extern int cjson_getlen(cjson_t array);
 56 
 57 /*
 58  * 根据索引得到这个数组中对象
 59  * array    : 数组对象
 60  * idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
 61  *            : 返回查找到的当前对象
 62  */
 63 extern cjson_t cjson_getarray(cjson_t array, int idx);
 64 
 65 /*
 66  * 根据key得到这个对象 相应位置的值
 67  * object    : 待处理对象中值
 68  * key        : 寻找的key
 69  *            : 返回 查找 cjson_t 对象
 70  */
 71 extern cjson_t cjson_getobject(cjson_t object, const char* key);
 72 
 73 
 74 // --------------------------------- 下面是 cjson 输出部分的处理代码 -----------------------------------------
 75 
 76 /*
 77  *  这里是将 cjson_t item 转换成字符串内容,需要自己free 
 78  * item        : cjson的具体结点
 79  *            : 返回生成的item的json串内容
 80  */
 81 extern char* cjson_print(cjson_t item);
 82 
 83 // --------------------------------- 下面是 cjson 输出部分的辅助代码 -----------------------------------------
 84 
 85 /*
 86  * 创建一个bool的对象 b==0表示false,否则都是true
 87  * b        : bool 值 最好是 _Bool
 88  *            : 返回 创建好的json 内容
 89  */
 90 extern cjson_t cjson_newbool(int b);
 91 extern cjson_t cjson_newnumber(double vd);
 92 extern cjson_t cjson_newstring(const char* vs);
 93 extern cjson_t cjson_newarray(void);
 94 extern cjson_t cjson_newobject(void);
 95 
 96 /*
 97  * 按照类型,创建 对映类型的数组 cjson对象
 98  *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING
 99  * type        : 类型目前支持 上面几种类型
100  * array    : 数组原始数据
101  * len        : 数组中元素长度
102  *            : 返回创建的数组对象
103  */
104 extern cjson_t cjson_newtypearray(int type, const void* array, int len);
105 
106 /*
107  * 将 jstr中 不需要解析的字符串都去掉
108  * jstr        : 待处理的json串
109  *            : 返回压缩后的json串内容
110  */
111 extern char* cjson_mini(char* jstr);
112 
113 /*
114  *    将json文件解析成json内容返回
115  * jpath    : json串路径
116  *            : 返回处理好的cjson_t 内容,失败返回NULL
117  */
118 extern cjson_t cjson_dofile(char* jpath);
119 
120 #endif // !_H_CJSON
View Code

cjson.c

  1 #include <cjson.h>
  2 #include <schead.h>
  3 #include <sclog.h>
  4 #include <tstring.h>
  5 #include <float.h>
  6 #include <math.h>
  7 
  8 // 删除cjson
  9 static void __cjson_delete(cjson_t c)
 10 {
 11     cjson_t next;
 12     while (c) {
 13         next = c->next;
 14         //递归删除儿子
 15         if (!(c->type & _CJSON_ISREF)) {
 16             if (c->child) //如果不是尾递归,那就先递归
 17                 __cjson_delete(c->child);
 18             if (c->vs)
 19                 free(c->vs);
 20         }
 21         else if (!(c->type & _CJSON_ISCONST) && c->key)
 22             free(c->key);
 23         free(c);
 24         c = next;
 25     }
 26 }
 27 
 28 /*
 29 *  删除json串内容,最近老是受清华的老学生打击, 会起来的......
 30 * c        : 待释放json_t串内容
 31 */
 32 void 
 33 cjson_delete(cjson_t* pc)
 34 {
 35     if (!pc || !*pc)
 36         return;
 37     __cjson_delete(*pc);
 38     *pc = NULL;
 39 }
 40 
 41 //构造一个空 cjson 对象
 42 static inline cjson_t __cjson_new(void)
 43 {
 44     cjson_t c = calloc(1, sizeof(struct cjson));
 45     if (!c) {
 46         SL_FATAL("calloc sizeof struct cjson error!");
 47         exit(_RT_EM);
 48     }
 49     return c;
 50 }
 51 
 52 // 简化的代码段,用宏来简化代码书写 , 16进制处理
 53 #define __parse_hex4_code(c, h) \
 54     if (c >= '0' && c <= '9') \
 55         h += c - '0'; \
 56     else if (c >= 'A' && c <= 'F') \
 57         h += 10 + c - 'A'; \
 58     else if (c >= 'a' && c <= 'z') \
 59         h += 10 + c - 'F'; \
 60     else \
 61         return 0
 62 
 63 // 等到unicode char代码
 64 static unsigned __parse_hex4(const char* str)
 65 {
 66     unsigned h = 0;
 67     char c = *str;
 68     //第一轮
 69     __parse_hex4_code(c, h);
 70     h <<= 4;
 71     c = *++str;
 72     //第二轮
 73     __parse_hex4_code(c, h);
 74     h <<= 4;
 75     c = *++str;
 76     //第三轮
 77     __parse_hex4_code(c, h);
 78     h <<= 4;
 79     c = *++str;
 80     //第四轮
 81     __parse_hex4_code(c, h);
 82 
 83     return h;
 84 }
 85 
 86 // 分析字符串的子函数,
 87 static const char* __parse_string(cjson_t item, const char* str)
 88 {
 89     static unsigned char __marks[] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
 90     const char *ptr;
 91     char *nptr, *out;
 92     int len;
 93     char c;
 94     unsigned uc, nuc;
 95 
 96     if (*str != '\"') { // 检查是否是字符串内容
 97         SL_WARNING("need \\\" str => %s error!", str);
 98         return NULL;
 99     }
100 
101     for (ptr = str + 1, len = 0; (c = *ptr++) != '\"' && c; ++len)
102         if (c == '\\') //跳过转义字符
103             ++ptr;
104     if (!(out = malloc(len + 1))) {
105         SL_FATAL("malloc %d size error!", len + 1);
106         return NULL;
107     }
108     // 这里复制拷贝内容
109     for (ptr = str + 1, nptr = out; (c = *ptr) != '\"' && c; ++ptr) {
110         if (c != '\\') {
111             *nptr++ = c;
112             continue;
113         }
114         // 处理转义字符
115         switch ((c = *++ptr)) {
116         case 'b': *nptr++ = '\b'; break;
117         case 'f': *nptr++ = '\f'; break;
118         case 'n': *nptr++ = '\n'; break;
119         case 'r': *nptr++ = '\r'; break;
120         case 't': *nptr++ = '\t'; break;
121         case 'u': // 将utf16 => utf8, 专门的utf处理代码
122             uc = __parse_hex4(ptr + 1);
123             ptr += 4;//跳过后面四个字符, unicode
124             if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0)    break;    /* check for invalid.    */
125 
126             if (uc >= 0xD800 && uc <= 0xDBFF)    /* UTF16 surrogate pairs.    */
127             {
128                 if (ptr[1] != '\\' || ptr[2] != 'u')    
129                     break;    /* missing second-half of surrogate.    */
130                 nuc = __parse_hex4(ptr + 3);
131                 ptr += 6;
132                 if (nuc < 0xDC00 || nuc>0xDFFF)        
133                     break;    /* invalid second-half of surrogate.    */
134                 uc = 0x10000 + (((uc & 0x3FF) << 10) | (nuc & 0x3FF));
135             }
136 
137             len = 4;
138             if (uc < 0x80) 
139                 len = 1;
140             else if (uc < 0x800) 
141                 len = 2;
142             else if (uc < 0x10000) 
143                 len = 3; 
144             nptr += len;
145 
146             switch (len) {
147             case 4: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
148             case 3: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
149             case 2: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
150             case 1: *--nptr = (uc | __marks[len]);
151             }
152             nptr += len;
153             break;
154         default: *nptr++ = c;
155         }
156     }
157 
158     *nptr = '\0';
159     if (c == '\"')
160         ++ptr;
161     item->vs = out;
162     item->type = _CJSON_STRING;
163     return ptr;
164 }
165 
166 // 分析数值的子函数,写的可以
167 static const char* __parse_number(cjson_t item, const char* str)
168 {
169     double n = 0.0, ns = 1.0, nd = 0.0; //n把偶才能值, ns表示开始正负, 负为-1, nd 表示小数后面位数
170     int e = 0, es = 1; //e表示后面指数, es表示 指数的正负,负为-1
171     char c;
172 
173     if ((c = *str) == '-' || c == '+') {
174         ns = c == '-' ? -1.0 : 1.0; //正负号检测, 1表示负数
175         ++str;
176     }
177     //处理整数部分
178     for (c = *str; c >= '0' && c <= '9'; c = *++str)
179         n = n * 10 + c - '0';
180     if (c == '.')
181         for (; (c = *++str) >= '0' && c <= '9'; --nd)
182             n = n * 10 + c - '0';
183 
184     // 处理科学计数法
185     if (c == 'e' || c == 'E') {
186         if ((c = *++str) == '+') //处理指数部分
187             ++str;
188         else if (c == '-')
189             es = -1, ++str;
190         for (; (c = *str) >= '0' && c <= '9'; ++str)
191             e = e * 10 + c - '0';
192     }
193 
194     //返回最终结果 number = +/- number.fraction * 10^+/- exponent
195     n = ns * n * pow(10.0, nd + es * e);
196     item->vd = n;
197     item->type = _CJSON_NUMBER;
198     return str;
199 }
200 
201 // 跳过不需要处理的字符
202 static const char* __skip(const char* in)
203 {
204     if (in && *in && *in <= 32) {
205         unsigned char c;
206         while ((c = *++in) && c <= 32)
207             ;
208     }
209     return in;
210 }
211 
212 // 递归下降分析 需要声明这些函数
213 static const char* __parse_array(cjson_t item, const char* str);
214 static const char* __parse_object(cjson_t item, const char* str);
215 static const char* __parse_value(cjson_t item, const char* value);
216 
217 // 分析数组的子函数, 采用递归下降分析
218 static const char* __parse_array(cjson_t item, const char* str)
219 {
220     cjson_t child;
221     if (*str != '[') {
222         SL_WARNING("array str error start: %s.", str);
223         return NULL;
224     }
225 
226     item->type = _CJSON_ARRAY;
227     str = __skip(str + 1);
228     if (*str == ']') // 低估提前结束
229         return str + 1;
230 
231     item->child = child = __cjson_new();
232     str = __skip(__parse_value(child, str));
233     if (!str) {//解析失败 直接返回
234         SL_WARNING("array str error e n d one: %s.", str);
235         return NULL;
236     }
237     while (*str == ',') {
238         cjson_t nitem = __cjson_new();
239         child->next = nitem;
240         nitem->prev = child;
241         child = nitem;
242         str = __skip(__parse_value(child, __skip(str + 1)));
243         if (!str) {// 写代码是一件很爽的事
244             SL_WARNING("array str error e n d two: %s.", str);
245             return NULL;
246         }
247     }
248 
249     if (*str != ']') {
250         SL_WARNING("array str error e n d: %s.", str);
251         return NULL;
252     }
253     return str + 1; // 跳过']'
254 }
255 
256 // 分析对象的子函数
257 static const char* __parse_object(cjson_t item, const char* str)
258 {
259     cjson_t child;
260     if (*str != '{') {
261         SL_WARNING("object str error start: %s.", str);
262         return NULL;
263     }
264 
265     item->type = _CJSON_OBJECT;
266     str = __skip(str + 1);
267     if (*str == '}')
268         return str + 1;
269 
270     //处理结点, 开始读取一个 key
271     item->child = child = __cjson_new();
272     str = __skip(__parse_string(child, str));
273     if (!str || *str != ':') {
274         SL_WARNING("__skip __parse_string is error : %s!", str);
275         return NULL;
276     }
277     child->key = child->vs;
278     child->vs = NULL;
279 
280     str = __skip(__parse_value(child, __skip(str + 1)));
281     if (!str) {
282         SL_WARNING("__skip __parse_string is error 2!");
283         return NULL;
284     }
285 
286     // 递归解析
287     while (*str == ',') {
288         cjson_t nitem = __cjson_new();
289         child->next = nitem;
290         nitem->prev = child;
291         child = nitem;
292         str = __skip(__parse_string(child, __skip(str + 1)));
293         if (!str || *str != ':'){
294             SL_WARNING("__parse_string need name or no equal ':' %s.", str);
295             return NULL;
296         }
297         child->key = child->vs;
298         child->vs = NULL;
299 
300         str = __skip(__parse_value(child, __skip(str+1)));
301         if (!str) {
302             SL_WARNING("__parse_string need item two ':' %s.", str);
303             return NULL;
304         }
305     }
306 
307     if (*str != '}') {
308         SL_WARNING("object str error e n d: %s.", str);
309         return NULL;
310     }
311     return str + 1;
312 }
313 
314 // 将value 转换塞入 item json值中一部分
315 static const char* __parse_value(cjson_t item, const char* value)
316 {
317     char c; 
318     if ((value) && (c = *value)) {
319         switch (c) {
320             // n = null, f = false, t = true
321         case 'n' : return item->type = _CJSON_NULL, value + 4;
322         case 'f' : return item->type = _CJSON_FALSE, value + 5;
323         case 't' : return item->type = _CJSON_TRUE, item->vd = 1.0, value + 4;
324         case '\"': return __parse_string(item, value);
325         case '0' : case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
326         case '+' : case '-': return __parse_number(item, value);
327         case '[' : return __parse_array(item, value);
328         case '{' : return __parse_object(item, value);
329         }
330     }
331     // 循环到这里是意外 数据
332     SL_WARNING("params value = %s!", value);
333     return NULL;
334 }
335 
336 /*
337 * 对json字符串解析返回解析后的结果
338 * jstr        : 待解析的字符串
339 *            : 返回解析好的字符串内容
340 */
341 cjson_t 
342 cjson_parse(const char* jstr)
343 {
344     cjson_t c = __cjson_new();
345     const char* end;
346 
347     if (!(end = __parse_value(c, __skip(jstr)))) {
348         SL_WARNING("__parse_value params end = %s!", end);
349         cjson_delete(&c);
350         return NULL;
351     }
352 
353     //这里是否检测 返回测试数据
354     return c;
355 }
356 
357 /*
358 * 根据 item当前结点的 next 一直寻找到 NULL, 返回个数
359 *推荐是数组使用
360 * array    : 待处理的cjson_t数组对象
361 *            : 返回这个数组中长度
362 */
363 int 
364 cjson_getlen(cjson_t array)
365 {
366     int len = 0;
367     if (array)
368         for (array = array->child; array; array = array->next)
369             ++len;
370 
371     return len;
372 }
373 
374 /*
375 * 根据索引得到这个数组中对象
376 * array    : 数组对象
377 * idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
378 *            : 返回查找到的当前对象
379 */
380 cjson_t 
381 cjson_getarray(cjson_t array, int idx)
382 {
383     cjson_t c;
384     DEBUG_CODE({
385         if (!array || idx < 0) {
386             SL_FATAL("array:%p, idx=%d params is error!", array, idx);
387             return NULL;
388         }
389     });
390 
391     for (c = array->child; c&&idx > 0; c = c->next)
392         --idx;
393 
394     return c;
395 }
396 
397 /*
398 * 根据key得到这个对象 相应位置的值
399 * object    : 待处理对象中值
400 * key        : 寻找的key
401 *            : 返回 查找 cjson_t 对象
402 */
403 cjson_t 
404 cjson_getobject(cjson_t object, const char* key)
405 {
406     cjson_t c;
407     DEBUG_CODE({
408         if (!object || !key || !*key) {
409             SL_FATAL("object:%p, key=%s params is error!", object, key);
410             return NULL;
411         }
412     });
413 
414     for (c = object->child; c && str_icmp(key, c->key); c = c->next)
415         ;
416 
417     return c;
418 }
419 
420 // --------------------------------- 下面是 cjson 输出部分的处理代码 -----------------------------------------
421 
422 // 2^n>=x , n是最小的整数
423 static int __pow2gt(int x)
424 {
425     --x;
426     x |= x >> 1;
427     x |= x >> 2;
428     x |= x >> 4;
429     x |= x >> 8;
430     x |= x >> 16;
431     return x + 1;
432 }
433 
434 /*
435  *     这里使用 tstring 结构 size 这里表示 字符串总大小,没有变化
436  * len 表示当前字符串的字符串起始偏移量 即 tstring->str + tstring->len 起始的
437  */
438 static char* __ensure(tstring p, int need)
439 {
440     char* nbuf;
441     int nsize;
442     if (!p || !p->str) {
443         SL_FATAL("p:%p need:%p is error!", p, need);
444         return NULL;
445     }
446     need += p->len;
447     if (need <= p->size) //内存够用直接返回结果
448         return p->str + p->len;
449     nsize = __pow2gt(need);
450     if ((nbuf = malloc(nsize*sizeof(char))) == NULL) {
451         free(p->str);
452         p->size = p->len = 0;
453         p->str = NULL;
454         SL_FATAL("malloc nsize = %d error!", nsize);
455         return NULL;
456     }
457     //这里复制内容
458     memcpy(nbuf, p->str, p->size);
459     free(p->str);
460     p->size = nsize;
461     p->str = nbuf;
462     return nbuf + p->len;
463 }
464 
465 // 这里更新一下 当前字符串, 返回当前字符串的长度
466 inline static int __update(tstring p) 
467 {
468     return (!p || !p->str) ? 0 : p->len + strlen(p->str+p->len);
469 }
470 
471 // 将item 中值转换成字符串 保存到p中
472 static char* __print_number(cjson_t item, tstring p)
473 {
474     char* str = NULL;
475     double d = item->vd;
476     int i = (int)d;
477     
478     if (d == 0) {  //普通0
479         str = __ensure(p, 2);
480         if (str)
481             str[0] = '0', str[1] = '\0';
482     }
483     else if ((fabs(d - i)) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) {
484         str = __ensure(p, 21); //int 值 
485         if (str)
486             sprintf(str, "%d", i);
487     }
488     else {
489         str = __ensure(p, 64); //double值 
490         if (str) {
491             double nd = fabs(d); //得到正值开始比较
492             if(fabs(floor(d) - d) <= DBL_EPSILON && nd < 1.0e60)
493                 sprintf(str, "%.0f", d);
494             else if(nd < 1.0e-6 || nd > 1.0e9) //科学计数法
495                 sprintf(str, "%e", d);
496             else
497                 sprintf(str, "%f", d);
498 
499         }
500     }
501 
502     return str;
503 }
504 
505 // 输出字符串内容
506 static char* __print_string(char* str, tstring p)
507 {
508     const char* ptr;
509     char *nptr, *out;
510     int len = 0, flag = 0;
511     unsigned char c;
512 
513     if (!str || !*str) { //最特殊情况,什么都没有 返回NULL
514         out = __ensure(p, 3);
515         if (!out)
516             return NULL;
517         out[0] = '\"', out[1] = '\"', out[2] = '\0';
518         return out;
519     }
520 
521 
522     for (ptr = str; (c=*ptr); ++ptr)
523         flag |= ((c > 0 && c < 32) || c == '\"' || c == '\\');
524     
525     if (!flag) {  //没有特殊字符直接处理结果
526         len = ptr - str;
527         out = __ensure(p,len + 3);
528         if (!out)
529             return NULL;
530         nptr = out;
531         *nptr++ = '\"';
532         strcpy(nptr, str);
533         nptr[len] = '\"';
534         nptr[len + 1] = '\0';
535         return out;
536     }
537 
538     //处理 存在 "和转义字符内容
539     for (ptr = str; (c = *ptr) && ++len; ++ptr) {
540         if (strchr("\"\\\b\f\n\r\t", c))
541             ++len;
542         else if (c < 32) //隐藏字符的处理, 这里可以改
543             len += 5;
544     }
545 
546     if ((out = __ensure(p, len + 3)) == NULL)
547         return NULL;
548     //先添加 \"
549     nptr = out;
550     *nptr++ = '\"';
551     for (ptr = str; (c = *ptr); ++ptr) {
552         if (c > 31 && c != '\"' && c != '\\') {
553             *nptr++ = c;
554             continue;
555         }
556         *nptr++ = '\\';
557         switch (c){
558         case '\\':    *nptr++ = '\\';    break;
559         case '\"':    *nptr++ = '\"';    break;
560         case '\b':    *nptr++ = 'b';    break;
561         case '\f':    *nptr++ = 'f';    break;
562         case '\n':    *nptr++ = 'n';    break;
563         case '\r':    *nptr++ = 'r';    break;
564         case '\t':    *nptr++ = 't';    break;
565         default: sprintf(nptr, "u%04x", c);nptr += 5;    /* 不可见字符 采用 4字节字符编码 */
566         }
567     }
568     *nptr++ = '\"';
569     *nptr = '\0';
570     return out;
571 }
572 
573 //这里是 递归下降 的函数声明处, 分别是处理值, 数组, object
574 static char* __print_value(cjson_t item, tstring p);
575 static char* __print_array(cjson_t item, tstring p);
576 static char* __print_object(cjson_t item, tstring p);
577 
578 // 定义实现部分, 内部私有函数 认为 item 和 p都是存在的
579 static char* __print_value(cjson_t item, tstring p) 
580 {
581     char* out = NULL;
582     switch ((item->type) & UCHAR_MAX) { // 0xff
583     case _CJSON_FALSE: if ((out = __ensure(p, 6))) strcpy(out, "false"); break;
584     case _CJSON_TRUE: if ((out = __ensure(p, 5))) strcpy(out, "true"); break;
585     case _CJSON_NULL: if ((out = __ensure(p, 5))) strcpy(out, "null"); break;
586     case _CJSON_NUMBER:    out = __print_number(item, p); break;
587     case _CJSON_STRING:    out = __print_string(item->vs, p); break;
588     case _CJSON_ARRAY:    out = __print_array(item, p); break;
589     case _CJSON_OBJECT:    out = __print_object(item, p); break;
590     }
591 
592     return out;
593 }
594 
595 // 同样 假定 item 和 p都是存在且不为NULL
596 static char* __print_array(cjson_t item, tstring p)
597 {
598     char* ptr;
599     cjson_t child = item->child;
600     int ncut, i;
601     // 得到孩子结点的深度
602     for (ncut = 0; (child); child = child->child)
603         ++ncut;
604     if (!ncut) { //没有孩子结点 直接空数组返回结果
605         char* out = NULL;
606         if (!(out = __ensure(p, 3))) 
607             strcpy(out, "[]");
608         return out;
609     }
610 
611     i = p->len;
612     if (!(ptr = __ensure(p, 1)))
613         return NULL;
614     *ptr = '[';
615     ++p->len;
616     for (child = item->child; (child); child = child->next) {
617         __print_value(child, p);
618         p->len = __update(p);
619         if (child->next) {
620             if (!(ptr = __ensure(p, 2)))
621                 return NULL;
622             *ptr++ = ',';
623             *ptr = '\0';
624             p->len += 1;
625         }
626     }
627     if (!(ptr = __ensure(p, 2)))
628         return NULL;
629     *ptr++ = ']';
630     *ptr = '\0';
631     return p->str + i;
632 
633 }
634 
635 // 同样 假定 item 和 p都是存在且不为NULL, 相信这些代码是安全的
636 static char* __print_object(cjson_t item, tstring p)
637 {
638     char* ptr;
639     int i, ncut, len;
640     cjson_t child = item->child;
641 
642     // 得到孩子结点的深度
643     for (ncut = 0; child; child = child->child)
644         ++ncut;
645     if (!ncut) {
646         char* out = NULL;
647         if (!(out = __ensure(p, 3)))
648             strcpy(out, "{}");
649         return out;
650     }
651 
652     i = p->len;
653     if (!(ptr = __ensure(p, 2)))
654         return NULL;
655     *ptr++ = '{';
656     *ptr -= '\0';
657     p->len += 1;
658     // 根据子结点 处理
659     for (child = item->child; (child); child = child->next) {
660         __print_string(child->key, p);
661         p->len = __update(p);
662 
663         //加入一个冒号
664         if (!(ptr = __ensure(p, 1)))
665             return NULL;
666         *ptr++ = ':';
667         p->len += 1;
668 
669         //继续打印一个值
670         __print_value(child, p);
671         p->len = __update(p);
672 
673         //结算最后内容
674         len = child->next ? 1 : 0;
675         if ((ptr = __ensure(p, len + 1)) == NULL)
676             return NULL;
677         if (child->next)
678             *ptr++ = ',';
679         *ptr = '\0';
680         p->len += len;
681     }
682     if (!(ptr = __ensure(p, 2)))
683         return NULL;
684     *ptr++ = '}';
685     *ptr = '\0';
686     return p->str + i;
687 }
688 
689 #define _INT_CJONSTR    (256)
690 /*
691 *  这里是将 cjson_t item 转换成字符串内容,需要自己free
692 * item        : cjson的具体结点
693 *            : 返回生成的item的json串内容
694 */
695 char* 
696 cjson_print(cjson_t item)
697 {
698     struct tstring p;
699     char* out;
700     if ((!item) || !(p.str = malloc(sizeof(char)*_INT_CJONSTR))) {
701         SL_FATAL("item:%p, p.str = malloc is error!", item);
702         return NULL;
703     }
704     p.size = _INT_CJONSTR;
705     p.len = 0;
706 
707     out = __print_value(item, &p); //从值处理开始, 返回最终结果
708     if (out == NULL) {
709         free(p.str);
710         SL_FATAL("__print_value item:%p, p:%p is error!", item, &p);
711         return NULL;
712     }
713     return realloc(out,strlen(out) + 1); // 体积变小 realloc返回一定成功
714 }
715 
716 // --------------------------------- 下面是 cjson 输出部分的辅助代码 -----------------------------------------
View Code

到这里基本前期主场都完了. 后面就是玩测试了.

 

4. 展示 cjson 测试部分

首先展示 test_cjson_write.c 测试脚本

#include <schead.h>
#include <sclog.h>
#include <cjson.h>

// 测试 cjson 函数
int main(int argc, char* argv[])
{
    //注册等待函数
    INIT_PAUSE();

    //启动日志记录功能
    sl_start();

    // 测试json 串
    char jstr[] = "{\n\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n\"format\": {\"type\":[1, 3, 4, 5.66], \n\"height\":     1080, \n\"interlace\":  false}\n}";

    printf("源码串 :\n %s\n", jstr);

    // 先生成 json 对象
    cjson_t root = cjson_parse(jstr);
    if (root == NULL) {
        puts("jstr 解析失败! 程序退出中....");
        exit(EXIT_FAILURE);
    }

    //这里简单测试输出内容
    char* njstr = cjson_print(root);

    if (njstr == NULL) {
        puts("输出内容失败,程序退出中!");
        cjson_delete(&root);
        exit(EXIT_FAILURE);
    }

    //合法范围直接输出 内容
    printf("解析串 :\n %s\n", njstr);

    //解析完需要释放
    free(njstr);

    //解析好 一定要注意释放操作
    cjson_delete(&root);


    //另一个测试 输出内存值
    printf("d = %d\n", strlen("{\"name\":\"Jack (\\\"Bee\\\") Nimble\",\"format\":{\"type\":[1,3,4,5.660000],\"height\":1080,\"interlace\":false}}"));
}

需要大家写一遍或看三遍. 测试结果 如下:

一切正常. 这里输出是可以的. 欢迎大家尝试了.

 

5.下次来个 cjson 较完整demo

  下次写好这个cjson库, 我们 测试一个下面 json 文件 实战解析一下

firefighting_rule.json 

{     
"firefighting_rule":     
 {    
  "key1":   
   {  
    "id":1,
    "dungeon_id":40008,
    "level_contain":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],
    "active_time":[[1,"110000"],[4,"110000"],[5,"210000"]],
    "boss_ui_head":"UI_icon/IMG_ShiJieBoss_TouXiang.png",
    "activity_tag_icon":"IMG_GaiBan_HuoDong_ShiJieBoss_TuBiao.png",
    "activity_tag_word":"IMG_GaiBan_ZhuCheng_ShiJieBoss_TuBiao_MingZi.png",
    "activity_pic_json":"UI_HuoDong_ShiJieBoss.json",
    "jinbi_buff_icon":"UI_icon/IMG_WorldBoss_JinbiBuff_Atk.png",
    "jinbi_buff_damage":[[8,1000],[9,1000],[11,1000],[12,1000]],
    "jinbi_buff_price":10,
    "jinbi_buff_limit":999,
    "free_change":1,
    "refresh_price":20,
    "change_price":20,
    "show_hero_num":20 
   }  
 }    
}     

解析成我们想要的那样的东西. 尝试, 追求, 按部就班 , 人生...........................................

 

后记 

  错误是难免,欢迎批评指正. 下次会对cjson 进行简单总结, 再添加一些辅助函数. 一切会从简单来.

posted on 2016-02-28 20:53  喜欢兰花山丘  阅读(1007)  评论(0编辑  收藏  举报