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()
主要特点:
支持格式化输出(缩进和换行)
自动扩展缓冲区
处理字符串转义
处理数字精度
支持 Unicode
处理循环引用
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;
}
处理流程:
解析 16 进制码:从
\uXXXX
中提取 4 位 16 进制数检测代理对:如果是高代理 (0xD800-0xDBFF),需要处理代理对
计算码点:单个字符或从代理对计算 Unicode 码点
UTF-8 编码:将码点编码为 1-4 字节的 UTF-8 序列
输出写入:将 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;
}
}
比较逻辑:
基本检查:空指针和类型不匹配直接返回 false
简单类型:null、true、false 只需类型相同即可
数值比较:使用
compare_double()
处理浮点数精度问题字符串比较:使用
strcmp()
进行字符串比较数组比较:逐个元素递归比较,长度必须相同
对象比较:检查所有键值对都存在且相等(双向检查)
性能注意:对象比较的时间复杂度为 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 内存管理优化
使用对象池:频繁创建销毁时,考虑使用对象池
预分配缓冲区:已知输出大小时,使用
cJSON_PrintBuffered()
自定义分配器:使用
cJSON_InitHooks()
设置更高效的分配器
14.2 解析优化
指定长度:已知输入长度时使用
cJSON_ParseWithLength()
避免深度嵌套:超过 1000 层会被拒绝
及时释放:解析后及时调用
cJSON_Delete()
14.3 生成优化
选择合适的格式:不需要可读性时使用
cJSON_PrintUnformatted()
预估大小:使用
cJSON_PrintBuffered()
时合理预估缓冲区大小引用机制:共享数据时使用引用避免复制
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 的源码结构、实现机制和使用方法,应该能帮助你更好地理解和使用这个库。