首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言 JSON 庫實戰 (二):構造輸出引擎完整實現 (遞歸下降生成)

     編輯:關於C語言

JSON 生成器:遞歸下降構造字符串

繼解析器之後,本文實現 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;

關鍵輔助函數

1. 緩衝區擴容 __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;
}

2. 數值格式化 __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、流式生成器 (大對象分塊輸出)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved