cJSON 源码解析

发布于:2025-06-22 ⋅ 阅读:(16) ⋅ 点赞:(0)

1. 概述

cJSON 是一个轻量级的 C 语言 JSON 解析库,支持 JSON 数据的解析和生成。它采用单一头文件和源文件的设计,易于集成到项目中。

主要特性

  • 完整的 JSON 支持(解析和生成)

  • 内存管理自动化

  • 支持格式化输出

  • 支持自定义内存分配器

  • 跨平台兼容

2. 核心数据结构

2.1 cJSON 结构体

typedef struct cJSON
{
    struct cJSON *next;      // 指向下一个兄弟节点
    struct cJSON *prev;      // 指向上一个兄弟节点  
    struct cJSON *child;     // 指向第一个子节点(用于数组和对象)

    int type;               // 节点类型

    char *valuestring;      // 字符串值(当 type 为 cJSON_String 时)
    int valueint;          // 整数值(已废弃,建议使用 valuedouble)
    double valuedouble;    // 数值(当 type 为 cJSON_Number 时)

    char *string;          // 键名(当节点是对象的成员时)
} cJSON;

这个结构体使用链表来表示 JSON 数据:

  • next/prev: 形成双向链表,用于表示数组元素或对象成员

  • child: 指向子节点,用于表示数组或对象的内容

  • type: 标识节点类型

  • valuestring/valuedouble: 存储实际值

  • string: 存储对象成员的键名

2.2 JSON 类型定义

#define cJSON_Invalid (0)
#define cJSON_False  (1 << 0)    // 1
#define cJSON_True   (1 << 1)    // 2
#define cJSON_NULL   (1 << 2)    // 4
#define cJSON_Number (1 << 3)    // 8
#define cJSON_String (1 << 4)    // 16
#define cJSON_Array  (1 << 5)    // 32
#define cJSON_Object (1 << 6)    // 64
#define cJSON_Raw    (1 << 7)    // 128 (原始 JSON)

// 特殊标志
#define cJSON_IsReference 256      // 引用标志,不会被释放
#define cJSON_StringIsConst 512    // 常量字符串标志

使用位操作定义类型,便于进行类型检查和组合操作。

2.3 内存管理钩子

typedef struct cJSON_Hooks
{
    void *(CJSON_CDECL *malloc_fn)(size_t sz);
    void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;

允许用户自定义内存分配和释放函数。

3. 核心解析结构

3.1 解析缓冲区

typedef struct
{
    const unsigned char *content;  // JSON 字符串内容
    size_t length;                // 总长度
    size_t offset;                // 当前解析位置
    size_t depth;                 // 当前嵌套深度
    internal_hooks hooks;         // 内存管理钩子
} parse_buffer;

3.2 打印缓冲区

typedef struct
{
    unsigned char *buffer;        // 输出缓冲区
    size_t length;               // 缓冲区总长度
    size_t offset;               // 当前写入位置
    size_t depth;                // 当前缩进深度
    cJSON_bool noalloc;          // 是否禁止重新分配
    cJSON_bool format;           // 是否格式化输出
    internal_hooks hooks;        // 内存管理钩子
} printbuffer;

4. 主要 API 函数分类

4.1 解析相关

  • cJSON_Parse(): 解析 JSON 字符串

  • cJSON_ParseWithLength(): 指定长度解析

  • cJSON_ParseWithOpts(): 带选项解析

  • cJSON_ParseWithLengthOpts(): 指定长度和选项解析

4.2 生成相关

  • cJSON_Print(): 生成格式化的 JSON 字符串

  • cJSON_PrintUnformatted(): 生成紧凑的 JSON 字符串

  • cJSON_PrintBuffered(): 使用预分配缓冲区生成

  • cJSON_PrintPreallocated(): 使用用户提供的缓冲区

4.3 创建相关

  • cJSON_CreateNull(): 创建 null 值

  • cJSON_CreateTrue()/cJSON_CreateFalse(): 创建布尔值

  • cJSON_CreateNumber(): 创建数值

  • cJSON_CreateString(): 创建字符串

  • cJSON_CreateArray()/cJSON_CreateObject(): 创建数组/对象

4.4 访问相关

  • cJSON_GetArraySize(): 获取数组大小

  • cJSON_GetArrayItem(): 获取数组元素

  • cJSON_GetObjectItem(): 获取对象成员

  • cJSON_HasObjectItem(): 检查对象是否包含指定键

4.5 修改相关

  • cJSON_AddItemToArray(): 向数组添加元素

  • cJSON_AddItemToObject(): 向对象添加成员

  • cJSON_DetachItemFromArray(): 从数组分离元素

  • cJSON_DeleteItemFromArray(): 从数组删除元素

  • cJSON_ReplaceItemInArray(): 替换数组元素

5. 解析机制详解

5.1 解析流程

解析过程是递归下降解析,主要函数调用链:

cJSON_Parse()
  ↓
cJSON_ParseWithLengthOpts()
  ↓
parse_value()
  ↓
parse_string() / parse_number() / parse_array() / parse_object()

5.2 核心解析函数

5.2.1 parse_value() - 值解析
static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
{
    // 跳过空白字符后,根据首字符判断类型
    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
        return parse_null();
    if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 5) == 0))
        return parse_false();
    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
        return parse_true();
    if (buffer_at_offset(input_buffer)[0] == '\"')
        return parse_string();
    if (is_number_start())
        return parse_number();
    if (buffer_at_offset(input_buffer)[0] == '[')
        return parse_array();
    if (buffer_at_offset(input_buffer)[0] == '{')
        return parse_object();
}
5.2.2 parse_string() - 字符串解析

字符串解析需要处理转义字符和 Unicode 编码:

static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
{
    const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
    const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
    unsigned char *output_pointer = NULL;
    unsigned char *output = NULL;

    /* 不是字符串 */
    if (buffer_at_offset(input_buffer)[0] != '\"')
    {
        goto fail;
    }

    {
        /* 计算输出大小(过度估计) */
        size_t allocation_length = 0;
        size_t skipped_bytes = 0;
        while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
        {
            /* 处理转义序列 */
            if (input_end[0] == '\\')
            {
                if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
                {
                    goto fail;
                }
                skipped_bytes++;
                input_end++;
            }
            input_end++;
        }
        if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
        {
            goto fail; /* 字符串意外结束 */
        }

        /* 这是输出所需的最大空间 */
        allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
        output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
        if (output == NULL)
        {
            goto fail; /* 分配失败 */
        }
    }

    output_pointer = output;
    /* 遍历字符串字面量 */
    while (input_pointer < input_end)
    {
        if (*input_pointer != '\\')
        {
            *output_pointer++ = *input_pointer++;
        }
        /* 转义序列 */
        else
        {
            unsigned char sequence_length = 2;
            if ((input_end - input_pointer) < 1)
            {
                goto fail;
            }

            switch (input_pointer[1])
            {
                case 'b':
                    *output_pointer++ = '\b';
                    break;
                case 'f':
                    *output_pointer++ = '\f';
                    break;
                case 'n':
                    *output_pointer++ = '\n';
                    break;
                case 'r':
                    *output_pointer++ = '\r';
                    break;
                case 't':
                    *output_pointer++ = '\t';
                    break;
                case '\"':
                case '\\':
                case '/':
                    *output_pointer++ = input_pointer[1];
                    break;

                /* UTF-16 字面量 */
                case 'u':
                    sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
                    if (sequence_length == 0)
                    {
                        /* 转换 UTF16 字面量到 UTF-8 失败 */
                        goto fail;
                    }
                    break;

                default:
                    goto fail;
            }
            input_pointer += sequence_length;
        }
    }

    /* 零终止输出 */
    *output_pointer = '\0';

    item->type = cJSON_String;
    item->valuestring = (char*)output;

    input_buffer->offset = (size_t) (input_end - input_buffer->content);
    input_buffer->offset++;

    return true;

fail:
    if (output != NULL)
    {
        input_buffer->hooks.deallocate(output);
    }

    if (input_pointer != NULL)
    {
        input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
    }

    return false;
}

转义字符处理:

  • \""

  • \\\

  • \//

  • \b → 退格

  • \f → 换页

  • \n → 换行

  • \r → 回车

  • \t → 制表符

  • \uXXXX → Unicode 字符

5.2.3 parse_number() - 数字解析
static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
{
    // 1. 识别数字字符(0-9, +, -, e, E, .)
    // 2. 复制到临时缓冲区
    // 3. 处理本地化小数点(将 '.' 替换为本地小数点)
    // 4. 使用 strtod() 转换
    // 5. 设置 valuedouble 和 valueint(考虑溢出)
}
5.2.4 parse_array() - 数组解析
static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
{
    // 1. 检查深度限制(防止栈溢出)
    // 2. 检查开始方括号 '['
    // 3. 处理空数组情况
    // 4. 循环解析数组元素:
    //    - 创建新节点
    //    - 递归调用 parse_value()
    //    - 添加到链表
    //    - 检查逗号分隔符
    // 5. 检查结束方括号 ']'
}
5.2.5 parse_object() - 对象解析
static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
{
    // 1. 检查深度限制
    // 2. 检查开始大括号 '{'
    // 3. 处理空对象情况
    // 4. 循环解析对象成员:
    //    - 解析键名(必须是字符串)
    //    - 检查冒号 ':'
    //    - 递归解析值
    //    - 添加到链表
    //    - 检查逗号分隔符
    // 5. 检查结束大括号 '}'
}

5.3 缓冲区管理

缓冲区检查宏
#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
空白字符跳过
static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
{
    while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
    {
       buffer->offset++;
    }
    return buffer;
}

6. 打印机制详解

6.1 打印缓冲区

typedef struct
{
    unsigned char *buffer;     // 缓冲区指针
    size_t length;            // 缓冲区长度
    size_t offset;            // 当前偏移量
    size_t depth;             // 当前嵌套深度(用于格式化打印)
    cJSON_bool noalloc;       // 是否禁止重新分配
    cJSON_bool format;        // 是否格式化输出
    internal_hooks hooks;     // 内存管理钩子
} printbuffer;

6.2 print_object() - 对象打印

static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
{
    unsigned char *output_pointer = NULL;
    size_t length = 0;
    cJSON *current_item = item->child;

    if (output_buffer == NULL)
    {
        return false;
    }

    /* 组合输出 */
    length = (size_t) (output_buffer->format ? 2 : 1); // fmt: {\n
    output_pointer = ensure(output_buffer, length + 1);
    if (output_pointer == NULL)
    {
        return false;
    }

    *output_pointer++ = '{';
    output_buffer->depth++;
    if (output_buffer->format)
    {
        *output_pointer++ = '\n';
    }
    output_buffer->offset += length;

    while (current_item)
    {
        if (output_buffer->format)
        {
            size_t i;
            output_pointer = ensure(output_buffer, output_buffer->depth);
            if (output_pointer == NULL)
            {
                return false;
            }
            for (i = 0; i < output_buffer->depth; i++)
            {
                *output_pointer++ = '\t';
            }
            output_buffer->offset += output_buffer->depth;
        }

        /* 打印键名 */
        if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
        {
            return false;
        }
        update_offset(output_buffer);

        length = (size_t) (output_buffer->format ? 2 : 1);
        output_pointer = ensure(output_buffer, length);
        if (output_pointer == NULL)
        {
            return false;
        }
        *output_pointer++ = ':';
        if (output_buffer->format)
        {
            *output_pointer++ = '\t';
        }
        output_buffer->offset += length;

        /* 打印值 */
        if (!print_value(current_item, output_buffer))
        {
            return false;
        }
        update_offset(output_buffer);

        /* 打印逗号(如果不是最后一个) */
        length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
        output_pointer = ensure(output_buffer, length + 1);
        if (output_pointer == NULL)
        {
            return false;
        }
        if (current_item->next)
        {
            *output_pointer++ = ',';
        }

        if (output_buffer->format)
        {
            *output_pointer++ = '\n';
        }
        *output_pointer = '\0';
        output_buffer->offset += length;

        current_item = current_item->next;
    }

    output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
    if (output_pointer == NULL)
    {
        return false;
    }
    if (output_buffer->format)
    {
        size_t i;
        for (i = 0; i < (output_buffer->depth - 1); i++)
        {
            *output_pointer++ = '\t';
        }
    }
    *output_pointer++ = '}';
    *output_pointer = '\0';
    output_buffer->depth--;

    return true;
}

6.3 打印函数调用流程

cJSON_Print()
    ↓
print()  // 创建打印缓冲区
    ↓
print_value()  // 根据类型调用相应打印函数
    ↓
print_string() / print_number() / print_array() / print_object()

主要特点:

  1. 支持格式化输出(缩进和换行)

  2. 自动扩展缓冲区

  3. 处理字符串转义

  4. 处理数字精度

  5. 支持 Unicode

  6. 处理循环引用

7. Unicode 处理机制

7.1 utf16_literal_to_utf8() - UTF-16 到 UTF-8 转换

这是处理 Unicode 转义序列 \uXXXX 的核心函数:

static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
{
    long unsigned int codepoint = 0;
    unsigned int first_code = 0;
    const unsigned char *first_sequence = input_pointer;
    unsigned char utf8_length = 0;
    unsigned char utf8_position = 0;
    unsigned char sequence_length = 0;
    unsigned char first_byte_mark = 0;

    if ((input_end - first_sequence) < 6)
    {
        /* 输入意外结束 */
        goto fail;
    }

    /* 获取第一个 utf16 序列 */
    first_code = parse_hex4(first_sequence + 2);

    /* 检查代码是否有效 */
    if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
    {
        goto fail;
    }

    /* UTF16 代理对 */
    if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
    {
        const unsigned char *second_sequence = first_sequence + 6;
        unsigned int second_code = 0;
        sequence_length = 12; /* \uXXXX\uXXXX */

        if ((input_end - second_sequence) < 6)
        {
            /* 输入意外结束 */
            goto fail;
        }

        if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
        {
            /* 缺少代理对的第二半部分 */
            goto fail;
        }

        /* 获取第二个 utf16 序列 */
        second_code = parse_hex4(second_sequence + 2);
        /* 检查代码是否有效 */
        if ((second_code < 0xDC00) || (second_code > 0xDFFF))
        {
            /* 代理对的第二半部分无效 */
            goto fail;
        }

        /* 从代理对计算 unicode 码点 */
        codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
    }
    else
    {
        sequence_length = 6; /* \uXXXX */
        codepoint = first_code;
    }

    /* 编码为 UTF-8
     * 最多需要 4 个字节编码:
     * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
    if (codepoint < 0x80)
    {
        /* 普通 ascii,编码 0xxxxxxx */
        utf8_length = 1;
    }
    else if (codepoint < 0x800)
    {
        /* 两个字节,编码 110xxxxx 10xxxxxx */
        utf8_length = 2;
        first_byte_mark = 0xC0; /* 11000000 */
    }
    else if (codepoint < 0x10000)
    {
        /* 三个字节,编码 1110xxxx 10xxxxxx 10xxxxxx */
        utf8_length = 3;
        first_byte_mark = 0xE0; /* 11100000 */
    }
    else if (codepoint <= 0x10FFFF)
    {
        /* 四个字节,编码 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
        utf8_length = 4;
        first_byte_mark = 0xF0; /* 11110000 */
    }
    else
    {
        /* 无效的 unicode 码点 */
        goto fail;
    }

    /* 编码为 utf8 */
    for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
    {
        /* 10xxxxxx */
        (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
        codepoint >>= 6;
    }
    /* 编码第一个字节 */
    if (utf8_length > 1)
    {
        (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
    }
    else
    {
        (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
    }

    *output_pointer += utf8_length;

    return sequence_length;

fail:
    return 0;
}

处理流程:

  1. 解析 16 进制码:从 \uXXXX 中提取 4 位 16 进制数

  2. 检测代理对:如果是高代理 (0xD800-0xDBFF),需要处理代理对

  3. 计算码点:单个字符或从代理对计算 Unicode 码点

  4. UTF-8 编码:将码点编码为 1-4 字节的 UTF-8 序列

  5. 输出写入:将 UTF-8 字节写入输出缓冲区

8. 内存管理

8.1 内存钩子机制

cJSON 支持自定义内存分配函数:

static internal_hooks global_hooks = { 
    internal_malloc, 
    internal_free, 
    internal_realloc 
};

CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
{
    if (hooks == NULL) {
        // 重置为默认钩子
        global_hooks.allocate = malloc;
        global_hooks.deallocate = free;
        global_hooks.reallocate = realloc;
    } else {
        // 设置自定义钩子
        global_hooks.allocate = hooks->malloc_fn ? hooks->malloc_fn : malloc;
        global_hooks.deallocate = hooks->free_fn ? hooks->free_fn : free;
        // 只有在使用标准 malloc/free 时才使用 realloc
        global_hooks.reallocate = 
            (global_hooks.allocate == malloc && global_hooks.deallocate == free) 
            ? realloc : NULL;
    }
}

8.2 节点创建和销毁

节点创建
static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
{
    cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
    if (node) {
        memset(node, '\0', sizeof(cJSON));  // 初始化为0
    }
    return node;
}
节点销毁
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
{
    cJSON *next = NULL;
    while (item != NULL) {
        next = item->next;
        
        // 递归删除子节点(如果不是引用)
        if (!(item->type & cJSON_IsReference) && (item->child != NULL)) {
            cJSON_Delete(item->child);
        }
        
        // 释放字符串值(如果不是引用)
        if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) {
            global_hooks.deallocate(item->valuestring);
        }
        
        // 释放键名(如果不是常量)
        if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) {
            global_hooks.deallocate(item->string);
        }
        
        global_hooks.deallocate(item);
        item = next;
    }
}

8.3 字符串复制

static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
{
    size_t length = 0;
    unsigned char *copy = NULL;

    if (string == NULL) return NULL;

    length = strlen((const char*)string) + sizeof("");
    copy = (unsigned char*)hooks->allocate(length);
    if (copy == NULL) return NULL;
    
    memcpy(copy, string, length);
    return copy;
}

9. 生成机制详解

9.1 生成流程

JSON 生成同样采用递归方式:

cJSON_Print()
  ↓
print()
  ↓
print_value()
  ↓
print_string() / print_number() / print_array() / print_object()

9.2 打印缓冲区管理

缓冲区扩展
static unsigned char* ensure(printbuffer * const p, size_t needed)
{
    if (needed > p->length - p->offset) {
        // 需要扩展缓冲区
        size_t newsize = needed * 2;  // 采用倍增策略
        
        if (p->hooks.reallocate != NULL) {
            // 使用 realloc 扩展
            newbuffer = p->hooks.reallocate(p->buffer, newsize);
        } else {
            // 手动重新分配
            newbuffer = p->hooks.allocate(newsize);
            memcpy(newbuffer, p->buffer, p->offset + 1);
            p->hooks.deallocate(p->buffer);
        }
        
        p->buffer = newbuffer;
        p->length = newsize;
    }
    
    return p->buffer + p->offset;
}

9.3 各类型值的生成

9.3.1 print_string() - 字符串生成
static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
{
    // 1. 统计需要转义的字符数量
    // 2. 分配足够的输出空间
    // 3. 添加开始引号
    // 4. 逐字符处理:
    //    - 普通字符直接复制
    //    - 特殊字符转义(\" \\ \n \r \t \b \f)
    //    - 控制字符转换为 \uXXXX 格式
    // 5. 添加结束引号
}
9.3.2 print_number() - 数字生成
static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
{
    double d = item->valuedouble;
    
    // 处理特殊值
    if (isnan(d) || isinf(d)) {
        return print_null();
    }
    
    // 整数优化
    if (d == (double)item->valueint) {
        sprintf(buffer, "%d", item->valueint);
    } else {
        // 浮点数:先尝试15位精度,如果精度不够则使用17位
        sprintf(buffer, "%1.15g", d);
        
        // 验证精度是否足够
        if (sscanf(buffer, "%lg", &test) != 1 || !compare_double(test, d)) {
            sprintf(buffer, "%1.17g", d);
        }
    }
    
    // 处理本地化小数点(将本地小数点替换为 '.')
    replace_decimal_point(buffer);
}
9.3.3 print_array() - 数组生成
static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
{
    // 1. 输出开始方括号 '['
    // 2. 遍历所有子元素:
    //    - 递归调用 print_value()
    //    - 元素间添加逗号分隔符
    //    - 格式化时添加空格
    // 3. 输出结束方括号 ']'
}
9.3.4 print_object() - 对象生成
static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
{
    // 1. 输出开始大括号 '{'
    // 2. 格式化时换行并增加缩进
    // 3. 遍历所有成员:
    //    - 输出键名(调用 print_string_ptr)
    //    - 输出冒号和空格
    //    - 递归调用 print_value() 输出值
    //    - 成员间添加逗号分隔符
    //    - 格式化时处理换行和缩进
    // 4. 格式化时减少缩进并换行
    // 5. 输出结束大括号 '}'
}

9.4 格式化控制

格式化输出会添加:

  • 对象和数组的换行

  • 适当的缩进(使用制表符)

  • 键值对间的空格

10. 高级特性

10.1 Unicode 支持

UTF-16 到 UTF-8 转换
static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, 
                                         const unsigned char * const input_end, 
                                         unsigned char **output_pointer)
{
    // 1. 解析 \uXXXX 格式的16进制数
    // 2. 处理 UTF-16 代理对(高代理 + 低代理)
    // 3. 转换为 UTF-8 编码:
    //    - 1字节:0xxxxxxx
    //    - 2字节:110xxxxx 10xxxxxx
    //    - 3字节:1110xxxx 10xxxxxx 10xxxxxx
    //    - 4字节:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
}

10.2 引用机制

cJSON 支持引用机制,避免深拷贝:

static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
{
    cJSON *reference = cJSON_New_Item(hooks);
    if (reference == NULL) return NULL;
    
    // 复制所有字段
    memcpy(reference, item, sizeof(cJSON));
    
    // 清除链表指针,设置引用标志
    reference->string = NULL;
    reference->type |= cJSON_IsReference;
    reference->next = reference->prev = NULL;
    
    return reference;
}

10.3 常量字符串优化

对于常量字符串,可以避免内存分配:

CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if (item != NULL) {
        item->type = cJSON_String | cJSON_IsReference;
        item->valuestring = (char*)cast_away_const(string);
    }
    return item;
}

10.4 循环检测

在复制时检测循环引用:

#define CJSON_CIRCULAR_LIMIT 10000

cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse)
{
    if(depth >= CJSON_CIRCULAR_LIMIT) {
        goto fail;  // 检测到可能的循环引用
    }
    // ... 递归复制子节点
    newchild = cJSON_Duplicate_rec(child, depth + 1, true);
}

10.5 JSON 压缩

CJSON_PUBLIC(void) cJSON_Minify(char *json)
{
    char *into = json;
    
    while (json[0] != '\0') {
        switch (json[0]) {
            case ' ': case '\t': case '\r': case '\n':
                json++;  // 跳过空白字符
                break;
                
            case '/':
                if (json[1] == '/') {
                    skip_oneline_comment(&json);   // 跳过单行注释
                } else if (json[1] == '*') {
                    skip_multiline_comment(&json); // 跳过多行注释
                } else {
                    json++;
                }
                break;
                
            case '\"':
                minify_string(&json, &into);  // 保持字符串不变
                break;
                
            default:
                into[0] = json[0];  // 复制其他字符
                json++;
                into++;
        }
    }
    
    *into = '\0';  // 添加结束符
}

11. 错误处理

11.1 全局错误状态

typedef struct {
    const unsigned char *json;
    size_t position;
} error;

static error global_error = { NULL, 0 };

CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
{
    return (const char*) (global_error.json + global_error.position);
}

11.2 安全检查

深度限制
#define CJSON_NESTING_LIMIT 1000

if (input_buffer->depth >= CJSON_NESTING_LIMIT) {
    return false; // 防止栈溢出
}
缓冲区边界检查
#define can_access_at_index(buffer, index) \
    ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))

12. 实用工具函数

12.1 类型检查

CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
{
    if (item == NULL) return false;
    return (item->type & 0xFF) == cJSON_String;
}

CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
{
    if (item == NULL) return false;
    return (item->type & 0xFF) == cJSON_Number;
}

12.2 值获取

CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
{
    if (!cJSON_IsString(item)) return NULL;
    return item->valuestring;
}

CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
{
    if (!cJSON_IsNumber(item)) return (double) NAN;
    return item->valuedouble;
}

12.3 比较函数

CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
{
    if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
    {
        return false;
    }

    /* 检查类型是否有效 */
    switch (a->type & 0xFF)
    {
        case cJSON_False:
        case cJSON_True:
        case cJSON_NULL:
        case cJSON_Number:
        case cJSON_String:
        case cJSON_Raw:
        case cJSON_Array:
        case cJSON_Object:
            break;

        default:
            return false;
    }

    /* 相同的对象相等 */
    if (a == b)
    {
        return true;
    }

    switch (a->type & 0xFF)
    {
        /* 在这些情况下,相等的类型就足够了 */
        case cJSON_False:
        case cJSON_True:
        case cJSON_NULL:
            return true;

        case cJSON_Number:
            if (compare_double(a->valuedouble, b->valuedouble))
            {
                return true;
            }
            return false;

        case cJSON_String:
        case cJSON_Raw:
            if ((a->valuestring == NULL) || (b->valuestring == NULL))
            {
                return false;
            }
            if (strcmp(a->valuestring, b->valuestring) == 0)
            {
                return true;
            }

            return false;

        case cJSON_Array:
        {
            cJSON *a_element = a->child;
            cJSON *b_element = b->child;

            for (; (a_element != NULL) && (b_element != NULL);)
            {
                if (!cJSON_Compare(a_element, b_element, case_sensitive))
                {
                    return false;
                }

                a_element = a_element->next;
                b_element = b_element->next;
            }

            /* 其中一个数组比另一个长 */
            if (a_element != b_element) {
                return false;
            }

            return true;
        }

        case cJSON_Object:
        {
            cJSON *a_element = NULL;
            cJSON *b_element = NULL;
            cJSON_ArrayForEach(a_element, a)
            {
                /* TODO 这有 O(n^2) 运行时,这很糟糕! */
                b_element = get_object_item(b, a_element->string, case_sensitive);
                if (b_element == NULL)
                {
                    return false;
                }

                if (!cJSON_Compare(a_element, b_element, case_sensitive))
                {
                    return false;
                }
            }

            /* 做两次,一次在 a 上,一次在 b 上,防止 a 是 b 的子集时的真比较
             * TODO: 用正确的方式做这个,这只是现在的一个修复 */
            cJSON_ArrayForEach(b_element, b)
            {
                a_element = get_object_item(a, b_element->string, case_sensitive);
                if (a_element == NULL)
                {
                    return false;
                }

                if (!cJSON_Compare(b_element, a_element, case_sensitive))
                {
                    return false;
                }
            }

            return true;
        }

        default:
            return false;
    }
}

比较逻辑:

  1. 基本检查:空指针和类型不匹配直接返回 false

  2. 简单类型:null、true、false 只需类型相同即可

  3. 数值比较:使用 compare_double() 处理浮点数精度问题

  4. 字符串比较:使用 strcmp() 进行字符串比较

  5. 数组比较:逐个元素递归比较,长度必须相同

  6. 对象比较:检查所有键值对都存在且相等(双向检查)

性能注意:对象比较的时间复杂度为 O(n²),大对象比较可能较慢。

13. 使用示例

13.1 基本解析

#include "cJSON.h"

int main() {
    const char *json_string = "{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}";
    
    // 解析 JSON
    cJSON *json = cJSON_Parse(json_string);
    if (json == NULL) {
        const char *error_ptr = cJSON_GetErrorPtr();
        if (error_ptr != NULL) {
            fprintf(stderr, "Error before: %s\n", error_ptr);
        }
        return 1;
    }
    
    // 获取字段值
    cJSON *name = cJSON_GetObjectItem(json, "name");
    cJSON *age = cJSON_GetObjectItem(json, "age");
    
    if (cJSON_IsString(name) && (name->valuestring != NULL)) {
        printf("Name: %s\n", name->valuestring);
    }
    
    if (cJSON_IsNumber(age)) {
        printf("Age: %d\n", age->valueint);
    }
    
    // 释放内存
    cJSON_Delete(json);
    return 0;
}

13.2 创建 JSON

int main() {
    // 创建根对象
    cJSON *json = cJSON_CreateObject();
    
    // 添加字段
    cJSON_AddStringToObject(json, "name", "John");
    cJSON_AddNumberToObject(json, "age", 30);
    
    // 创建数组
    cJSON *hobbies = cJSON_CreateArray();
    cJSON_AddItemToArray(hobbies, cJSON_CreateString("reading"));
    cJSON_AddItemToArray(hobbies, cJSON_CreateString("swimming"));
    cJSON_AddItemToObject(json, "hobbies", hobbies);
    
    // 生成 JSON 字符串
    char *json_string = cJSON_Print(json);
    printf("%s\n", json_string);
    
    // 释放内存
    free(json_string);
    cJSON_Delete(json);
    return 0;
}

13.3 遍历数组和对象

void print_json(cJSON *json) {
    cJSON *item = NULL;
    
    if (cJSON_IsObject(json)) {
        cJSON_ArrayForEach(item, json) {
            printf("Key: %s, ", item->string);
            print_json(item);  // 递归处理值
        }
    } else if (cJSON_IsArray(json)) {
        int index = 0;
        cJSON_ArrayForEach(item, json) {
            printf("Index %d: ", index++);
            print_json(item);  // 递归处理元素
        }
    } else if (cJSON_IsString(json)) {
        printf("String: %s\n", json->valuestring);
    } else if (cJSON_IsNumber(json)) {
        printf("Number: %g\n", json->valuedouble);
    }
}

14. 性能优化建议

14.1 内存管理优化

  1. 使用对象池:频繁创建销毁时,考虑使用对象池

  2. 预分配缓冲区:已知输出大小时,使用 cJSON_PrintBuffered()

  3. 自定义分配器:使用 cJSON_InitHooks() 设置更高效的分配器

14.2 解析优化

  1. 指定长度:已知输入长度时使用 cJSON_ParseWithLength()

  2. 避免深度嵌套:超过 1000 层会被拒绝

  3. 及时释放:解析后及时调用 cJSON_Delete()

14.3 生成优化

  1. 选择合适的格式:不需要可读性时使用 cJSON_PrintUnformatted()

  2. 预估大小:使用 cJSON_PrintBuffered() 时合理预估缓冲区大小

  3. 引用机制:共享数据时使用引用避免复制

15. 常见陷阱与注意事项

15.1 内存泄漏

  • 必须调用 cJSON_Delete() 释放解析结果

  • 必须调用 free() 释放打印结果

  • 引用类型节点不会释放引用的内容

15.2 字符串处理

  • valuestring 可能为 NULL,使用前检查

  • 修改 valuestring 后应该使用 cJSON_SetValuestring()

  • 常量字符串标记为 cJSON_StringIsConst 时不要修改

15.3 数值精度

  • valueint 已废弃,使用 valuedouble

  • 整数超出 INT_MAX 时会被截断

  • 浮点数使用 IEEE 754 双精度

15.4 并发安全

  • cJSON 不是线程安全的

  • 全局错误状态和内存钩子在多线程环境下需要保护

  • 不同线程操作同一个 cJSON 对象需要同步

此文档详细分析了 cJSON 的源码结构、实现机制和使用方法,应该能帮助你更好地理解和使用这个库。


网站公告

今日签到

点亮在社区的每一天
去签到