C json實戰引擎 二 , 實現構造部分,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 進行簡單總結, 再添加一些輔助函數. 一切會從簡單來.