繼解析器之後,本文實現 JSON 的**構造/輸出**引擎:將內存中的 cjson_t 樹結構序列化為標準 JSON 字符串。核心採用**遞歸下降**策略,配合動態字符串緩衝區 tstring 實現高效拼接。
struct cjson {
struct cjson *next, *prev; // 兄弟鏈表
struct cjson *child; // 子節點 (Object/Array)
int type; // 類型標記
char *key; // Object 成員名
char *vs; // String 值
double vd; // Number/Bool 值
};
typedef struct cjson* cjson_t;
// 動態字符串緩衝區
struct tstring {
char *str; int len; int size; // 內容、已用長度、總容量
};
typedef struct tstring* tstring;
__ensure (指數增長 + 2 的冪次對齊)static char* __ensure(tstring p, int need) {
need += p->len;
if (need <= p->size) return p->str + p->len;
int nsize = __pow2gt(need); // 返回 >= need 的最小 2 的冪
char *nbuf = malloc(nsize);
memcpy(nbuf, p->str, p->size);
free(p->str);
p->str = nbuf; p->size = nsize;
return nbuf + p->len;
}
static int __pow2gt(int x) { // Hacker's Delight 算法
--x; x |= x>>1; x |= x>>2; x |= x>>4; x |= x>>8; x |= x>>16; return x+1;
}
__print_number (智能選擇 int/double/科學計數法)static char* __print_number(cjson_t item, tstring p) {
double d = item->vd;
int i = (int)d;
char *str;
if (d == 0) { str = __ensure(p, 2); strcpy(str, "0"); }
else if (fabs(d - i) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) {
str = __ensure(p, 21); sprintf(str, "%d", i); // 純整數
} else {
str = __ensure(p, 64);
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;
}
// 類型分發
static char* __print_value(cjson_t item, tstring p) {
switch (item->type & 0xFF) {
case _CJSON_FALSE: return __ensure(p,6), strcpy(__ensure(p,6),"false");
case _CJSON_TRUE: return __ensure(p,5), strcpy(__ensure(p,5),"true");
case _CJSON_NULL: return __ensure(p,5), strcpy(__ensure(p,5),"null");
case _CJSON_NUMBER: return __print_number(item, p);
case _CJSON_STRING: return __print_string(item->vs, p);
case _CJSON_ARRAY: return __print_array(item, p);
case _CJSON_OBJECT: return __print_object(item, p);
}
return NULL;
}
// 數組生成: [v1,v2,...]
static char* __print_array(cjson_t item, tstring p) {
int i = p->len;
char *ptr = __ensure(p, 1); *ptr = '['; p->len++;
for (cjson_t c = item->child; c; c = c->next) {
__print_value(c, p); p->len = __update(p);
if (c->next) { ptr = __ensure(p, 2); *ptr++ = ','; *ptr = 0; p->len++; }
}
ptr = __ensure(p, 2); *ptr++ = ']'; *ptr = 0;
return p->str + i;
}
// 對象生成: {"k1":v1,"k2":v2}
static char* __print_object(cjson_t item, tstring p) {
int i = p->len;
char *ptr = __ensure(p, 2); *ptr++ = '{'; *ptr = 0; p->len++;
for (cjson_t c = item->child; c; c = c->next) {
__print_string(c->key, p); p->len = __update(p);
ptr = __ensure(p, 2); *ptr++ = ':'; *ptr = 0; p->len++;
__print_value(c, p); p->len = __update(p);
if (c->next) { ptr = __ensure(p, 2); *ptr++ = ','; *ptr = 0; p->len++; }
}
ptr = __ensure(p, 2); *ptr++ = '}'; *ptr = 0;
return p->str + i;
}
// 字符串轉義生成 (含 Unicode \uXXXX)
static char* __print_string(char *str, tstring p) { /* ... 處理 \ " \b \f \n \r \t \uXXXX ... */ }
// 對外接口
char* cjson_print(cjson_t item) {
struct tstring p = {0};
p.str = malloc(256); p.size = 256; p.len = 0;
char *out = __print_value(item, &p);
return realloc(out, strlen(out)+1); // 縮容返回精確大小
}
int main() {
char *json = "{\"name\":\"Jack\",\"format\":{\"type\":[1,3,4,5.66],\"height\":1080,\"interlace\":false}}";
cjson_t root = cjson_parse(json);
char *out = cjson_print(root);
printf("輸出: %s\n", out);
// 輸出: {"name":"Jack","format":{"type":[1,3,4,5.660000],"height":1080,"interlace":false}}
free(out); cjson_delete(&root);
return 0;
}
| 模塊 | 技術亮點 |
|---|---|
| 緩衝區 | tstring + __ensure 指數增長 + 2冪對齊,均攤 O(1) 擴容 |
| 數值格式化 | DBL_EPSILON 判斷整數、科學計數法閾值自動選擇、sprintf 安全 |
| 字符串轉義 | 完整處理 \b\f\n\r\t\"\\ + Unicode \uXXXX (UTF-16 轉 UTF-8) |
| 遞歸下降 | __print_value 分發 -> __print_array/__print_object 遞歸,結構清晰 |
| 內存管理 | 最終 realloc 縮容,調用者 free,無內存洩漏 |
JSON 生成器通過遞歸下降遍歷語法樹 + 動態緩衝區高效拼接,將內存對象序列化為標準文本。核心難點在於數值格式化精度控制、字符串轉義完整性、緩衝區擴容策略。本實現參考 cJSON 設計,代碼緊湊、無依賴、適合嵌入式/庫開發。下一步可擴展 cjson_mini (壓縮輸出)、cjson_new* 構造 API、流式生成器 (大對象分塊輸出)。