如果说互联网有一种”通用语言”,那一定是 JSON。几乎每一个现代 Web API 都在使用它,每一种主流编程语言都内置或拥有第三方 JSON 库,甚至连 NoSQL 数据库(如 MongoDB、CouchDB)都选择以 JSON(或其变体)作为原生存储格式。
然而,JSON 并非完美——它有着自己的局限性,也面临来自多种替代格式的竞争。更重要的是,在 C 语言这种没有反射机制、没有内置字符串类型的底层环境中,处理 JSON 一直是件苦差事。
本文将全面介绍 JSON 的历史、优缺点和竞争者,并重点展示如何使用 json-gen-c —— 一个先定义数据结构、再生成代码的 C 语言 JSON 工具 —— 来大幅简化 C 程序员处理 JSON 的工作流程。
1. JSON 简介与历史
1.1 什么是 JSON?
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它基于 JavaScript 的对象字面量语法,但与语言无关,任何编程语言都可以轻松解析和生成 JSON 数据。
一段典型的 JSON 数据如下:
{
"name": "张三",
"age": 30,
"is_programmer": true,
"languages": ["C", "Go", "Python"],
"address": {
"city": "深圳",
"country": "中国"
}
}JSON 的语法只有两种基本结构: -
对象(Object):由花括号 {}
包围的键值对集合 - 数组(Array):由方括号
[] 包围的有序值列表
值的类型可以是:字符串、数字、布尔值(true/false)、null、对象或数组。仅此而已。
1.2 历史演进
JSON 的诞生要从一个人说起——Douglas Crockford。
2001 年,Douglas Crockford 注意到 JavaScript 的对象字面量语法足够简洁,可以直接用作数据交换格式。他在 json.org 上正式命名并描述了这种格式,JSON 就此诞生。
“I discovered JSON. I do not claim to have invented it, because it already existed in nature.” — Douglas Crockford
有趣的是,Crockford 一直强调他是”发现”了 JSON,而不是”发明”了它。因为 JavaScript 的对象字面量语法早在 1999 年(ECMAScript 3)就已存在,他只是给它起了个名字并推广为通用数据格式。
以下是 JSON 发展的关键时间线:
| 年份 | 里程碑 |
|---|---|
| 2001 | Douglas Crockford 在 json.org 上提出 JSON |
| 2002 | JSON.org 网站上线,提供多语言解析器链接 |
| 2005 | Ajax 技术兴起,JSON 作为 XML 的替代方案走入主流 |
| 2006 | RFC 4627 发布,JSON 成为正式标准 |
| 2013 | ECMA-404 发布,JSON 获得 ECMA 标准化 |
| 2014 | RFC 7159 替代 RFC 4627,修正若干细节 |
| 2017 | RFC 8259 发布,成为当前最新 JSON 标准 |
从时间线可以看出,JSON 从一个”民间约定”逐步登堂入室,历经三个 RFC 版本的迭代,成为了互联网上最广泛使用的数据交换格式之一。
1.3 JSON 为何杀死了 XML(在 Web API 领域)
2005 年之前,Web 服务的数据交换几乎被 XML 垄断(还记得 SOAP 吗?)。但 Ajax(Asynchronous JavaScript and XML)技术兴起后,开发者们很快注意到一件事:
<!-- XML 表示一个用户 -->
<user>
<name>张三</name>
<age>25</age>
<active>true</active>
</user>{"name": "张三", "age": 25, "active": true}同样的数据,JSON 只需要 XML 大约
三分之一
的字符数。在带宽宝贵的年代,这个差距非常显著。更重要的是,在浏览器端解析
JSON 只需一行 JSON.parse(),而解析 XML
需要笨重的 DOM API。
到了 2010 年代,REST API 全面拥抱 JSON,XML 在 Web API 场景中的主导地位已明显被取代。
2. JSON 的优点
2.1 人类可读性
JSON 的语法设计得足够直观,即使是非程序员也能大致读懂。花括号代表对象,方括号代表数组,冒号分隔键值——这些符号的含义几乎不需要学习。
2.2 跨语言支持
截至目前,你能想到的几乎每一种编程语言都有成熟的 JSON 支持:
| 语言 | 内置/标准库 | 调用方式 |
|---|---|---|
| JavaScript | 内置 | JSON.parse() /
JSON.stringify() |
| Python | 标准库 | import json |
| Go | 标准库 | encoding/json |
| Java | 常用第三方 | Jackson / Gson |
| C++ | 常用第三方 | nlohmann/json / RapidJSON |
| Rust | 常用第三方 | serde_json |
| C | 第三方 | cJSON / Jansson / json-gen-c |
2.3 轻量且高效
JSON 的语法元素极少(花括号、方括号、冒号、逗号、引号),没有 XML 那样的闭合标签冗余。以同样的数据量计算,JSON 体积通常只有 XML 的 30%~50%。
2.4 自描述性
JSON 数据本身就携带了结构信息(字段名),不需要额外的 schema 文件就能理解数据含义。这一点让它非常适合快速原型开发和 API 调试。
2.5 嵌套表达力
JSON 原生支持嵌套结构——对象中可以嵌套对象和数组,数组中可以嵌套对象和数组,理论上深度无限。这使它能表达复杂的层次化数据。
3. JSON 的缺点
JSON 远非完美,以下是它的主要痛点:
3.1 不支持注释
这是 JSON 被诟病最多的一点。Douglas Crockford 故意在 JSON 规范中排除了注释,理由是:
“I removed comments from JSON because I saw people were using them to hold parsing directives.”
这个决定导致 JSON 不太适合作为配置文件格式。你无法在一个复杂的 JSON 配置中添加任何解释性文字。实际项目中,许多团队被迫使用 JSONC(JSON with Comments)或干脆改用 YAML/TOML。
3.2 没有日期/时间类型
JSON 只有字符串、数字、布尔、null 四种基本类型,没有专用的日期/时间类型。你只能用字符串表示,再自行约定格式(ISO 8601?Unix 时间戳?还是某种自定义格式?)。不同系统之间的日期格式不一致是 JSON API 中的常见坑。
3.3 数字精度问题
JSON 规范并未限定数字的精度和范围。在实践中,由于大多数 JSON 解析器使用 IEEE 754 双精度浮点数(64 位),超过 \(2^{53}\) 的整数会丢失精度。Twitter 当年就遇到了这个问题,不得不同时返回数字和字符串两种形式的 tweet ID。
{
"id": 9007199254740993,
"id_str": "9007199254740993"
}3.4 冗余度
每个对象中的每一条记录都要重复写字段名。如果你有一个包含
10000 条记录的数组,每条记录都有
"name"、"age"、"email"
这样的字段名,那光是字段名的重复就浪费了大量空间。
3.5 不支持二进制数据
JSON 是纯文本格式,无法直接嵌入二进制数据。要传输图片、文件等二进制内容,必须先 Base64 编码(体积增大约 33%),然后作为字符串嵌入。
3.6 缺乏标准 Schema
JSON 本身没有内建的 schema 机制。虽然 JSON Schema 规范存在,但它是可选的附加标准,远不如 XML Schema / DTD 在生态中的集成度。
4. JSON 的竞争者
JSON 通吃一切场景了吗?并非如此。不同场景下,有多种格式在与 JSON 竞争:
4.1 格式对比总览
| 格式 | 类型 | 人类可读 | 支持注释 | Schema 支持 | 性能 | 适用场景 |
|---|---|---|---|---|---|---|
| JSON | 文本 | [好] | [否] | 可选 (JSON Schema) | 中 | Web API、配置、通用数据交换 |
| XML | 文本 | [一般] | [是] | 强 (XSD/DTD) | 低 | 企业集成、文档标记、SOAP |
| YAML | 文本 | [极好] | [是] | 可选 | 低 | 配置文件(K8s、Docker Compose) |
| TOML | 文本 | [好] | [是] | 弱 | 中 | 简单配置文件(Cargo.toml) |
| Protobuf | 二进制 | [否] | N/A | 内建 (.proto) | 极高 | gRPC、高性能微服务通信 |
| MessagePack | 二进制 | [否] | N/A | 无 | 高 | 高吞吐量的 JSON 替代方案 |
| BSON | 二进制 | [否] | N/A | 弱 | 高 | MongoDB 存储 |
4.2 各竞争者简评
- XML — 在企业级 Java 应用(Spring、Maven POM、Android 布局)中仍大量使用。XSD 提供强类型验证,XPath 支持复杂查询,但冗长语法使其在 Web API 场景中逐渐让位于 JSON。
- YAML — 支持注释、可读性极高,是 DevOps
领域的事实标准(Kubernetes、GitHub
Actions、Ansible)。缺点是缩进敏感且存在隐式类型转换陷阱(
yes→true,NO→false)。 - TOML —
语法简单直觉,适合扁平配置文件(
Cargo.toml、pyproject.toml),但不擅长深度嵌套。 - Protocol Buffers — Google 推出的二进制序列化框架,先定义结构再生成代码,性能极高但数据不可读。gRPC 的默认格式。
- MessagePack — “二进制的 JSON”,数据模型与 JSON 一致但编码更紧凑、解析更快,适合高吞吐但不需要重型 schema 的场景。
- BSON — MongoDB 的原生存储格式,在 JSON 基础上扩展了日期、二进制等类型,但不是通用交换格式。
4.3 该如何选择?
下面这张图提供一条简单的决策路径:
5. C 语言处理 JSON:一个传统难题
前面说了 JSON 的种种好处,但对 C 程序员来说,使用 JSON 一直是一种折磨。原因很简单:
5.1 C 没有反射机制
像 Go 的 encoding/json 或 Python 的
json 模块可以自动遍历数据结构的字段并映射到
JSON 键名。C
做不到——编译后,所有结构体字段名信息就丢失了。
5.2 手写解析代码极其繁琐
假设你有这样一个结构体:
struct User {
int id;
char name[64];
double balance;
};使用常见的 C JSON 库(如 cJSON)来序列化和反序列化,你需要这样写:
// --- 序列化 ---
cJSON *root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "id", user.id);
cJSON_AddStringToObject(root, "name", user.name);
cJSON_AddNumberToObject(root, "balance", user.balance);
char *json_str = cJSON_Print(root);
cJSON_Delete(root);
// --- 反序列化 ---
cJSON *root = cJSON_Parse(json_str);
cJSON *id_item = cJSON_GetObjectItem(root, "id");
if (id_item) user.id = id_item->valueint;
cJSON *name_item = cJSON_GetObjectItem(root, "name");
if (name_item) {
strncpy(user.name, name_item->valuestring, sizeof(user.name) - 1);
user.name[sizeof(user.name) - 1] = '\0';
}
cJSON *balance_item = cJSON_GetObjectItem(root, "balance");
if (balance_item) user.balance = balance_item->valuedouble;
cJSON_Delete(root);才三个字段就已经这么多代码了。如果是一个有 20 个字段、其中还包含嵌套结构和数组的复杂类型呢?光是反序列化代码就可能长达数百行,而且每一行都是手写的、容易出错的。
5.3 错误处理是噩梦
漏了一个 NULL
检查?嵌套层级写错了?字段名拼写有误?——这些 bug
在编译期完全不会暴露,只会在运行时悄悄地给你处理出错误数据或段错误。
那么,有没有一种方式能让 C 程序员像 Go 语言那样,只定义数据结构,就自动获得全部的 JSON 序列化/反序列化能力呢?
答案是 json-gen-c。
6. json-gen-c:让 C 语言也能优雅地处理 JSON
json-gen-c 是一个代码生成器,它的理念很简单:
只描述一次数据结构,自动生成全部 JSON 操作代码。
6.1 核心设计理念
| 特性 | 说明 |
|---|---|
| 先定义结构,再生成代码 | 在 .json-gen-c
文件中定义结构体,一次描述,自动生成 C 代码 |
| 零运行时反射 | 所有映射逻辑在代码生成阶段确定,运行时无额外开销 |
| 线程安全 | 解析上下文使用显式结构体而非全局变量 |
| 配套齐全 | 自带轻量级 sstr 字符串库和数组辅助函数 |
| 便于接入 CI | 警告即错误,Make 目标在本地和 CI 环境行为一致 |
6.2 工作流程
整个流程只需三步:
- 定义:在
.json-gen-c文件中用类 C 语法描述数据结构 - 生成:运行
json-gen-c命令,自动生成.h和.c文件 - 使用:在你的代码中
#include "json.gen.h",直接调用生成的函数
6.3 安装
git clone https://github.com/zltl/json-gen-c.git
cd json-gen-c
make -j$(nproc)
sudo make install6.4 支持的数据类型
| 类型 | 说明 |
|---|---|
int |
整数 |
long |
长整数 |
float |
单精度浮点数 |
double |
双精度浮点数 |
bool |
布尔值 |
sstr_t |
动态字符串(安全替代 char*) |
| 自定义结构体 | 嵌套结构体引用 |
以上类型 + [] |
动态数组 |
6.5 生成的 API
对于每个定义的结构体 Foo,json-gen-c
自动生成以下函数:
// 初始化结构体(将所有字段置零/NULL)
int Foo_init(struct Foo *obj);
// 清理结构体(释放所有动态分配的内存)
int Foo_clear(struct Foo *obj);
// 序列化:结构体 -> JSON 字符串
int json_marshal_Foo(struct Foo *obj, sstr_t out);
// 序列化(带缩进的美观输出)
int json_marshal_indent_Foo(struct Foo *obj, int indent, int curindent, sstr_t out);
// 反序列化:JSON 字符串 -> 结构体
int json_unmarshal_Foo(sstr_t in, struct Foo *obj);
// 数组序列化 / 反序列化
int json_marshal_array_Foo(struct Foo *obj, int len, sstr_t out);
int json_unmarshal_array_Foo(sstr_t in, struct Foo **obj, int *len);完整的 API 文档请参考:json-gen-c Doxygen 文档
6.6 适用边界
json-gen-c 最适合结构稳定、类型明确、能够在编译前确定 JSON 完整字段布局的场景。典型的适用场景包括:协议消息、配置文件、游戏存档、设备上报数据等。
以下场景可能不太适合纯代码生成方案:
- 高度动态的 JSON:字段名和层级在运行时才确定(如用户自定义表单、插件系统的元数据)。
- 需要大量运行时自定义逻辑:例如根据版本号走不同的反序列化路径、条件性忽略某些字段。
- 只读/只写单方向场景且结构极简:如果只需要从 JSON 中取一两个字段,直接用 cJSON 反而更轻量,不值得引入代码生成。
对于这类场景,cJSON、Jansson 等运行时解析库仍然是合理的选择,json-gen-c 并不试图取代它们。
7. 完整实战示例:RPG 游戏角色存档系统
接下来用一个完整的项目——为一款 RPG 游戏实现角色存档的 JSON 序列化——来看看 json-gen-c 的实际效果。
之所以选择这个示例,是因为它同时覆盖了 C 语言处理 JSON
时的几乎所有典型难点:标量字段(int、double)、动态字符串(sstr_t)、嵌套结构体(角色→位置)、动态数组(背包物品列表)以及字符串数组(成就列表)的内存管理。如果
json-gen-c 能干净利落地处理这个结构,那日常遇到的绝大多数
JSON 场景它都能覆盖。
7.1 设计数据结构
一个角色存档需要保存: - 角色信息:名字、等级、HP/MP、职业 - 位置信息:地图坐标 (x, y) - 背包物品:物品名称、类型、数量、属性值 - 存档元信息:存档名、版本号
创建文件 game_save.json-gen-c:
// 地图位置
struct Position {
double x;
double y;
};
// 背包中的物品
struct Item {
sstr_t name;
sstr_t type;
int quantity;
double power;
};
// 游戏角色
struct Character {
sstr_t name;
sstr_t class_name;
int level;
int hp;
int max_hp;
int mp;
int max_mp;
double attack;
double defense;
Position position;
Item inventory[];
};
// 存档主结构
struct GameSave {
sstr_t save_name;
int version;
long play_time_seconds;
Character player;
sstr_t achievements[];
};这段定义文件展示了 json-gen-c
的全部表达能力:标量字段(int、double)、字符串(sstr_t)、嵌套结构体(Position、Character)和动态数组(Item inventory[]、sstr_t achievements[])。
7.2 生成代码
json-gen-c -in game_save.json-gen-c -out .这一条命令会生成: - json.gen.h —
结构体定义和函数声明 - json.gen.c —
全部序列化/反序列化实现 - sstr.h /
sstr.c — 字符串辅助库
7.3 编写主程序
创建 main.c(为突出核心流程,以下示例省略了
malloc
返回值检查等错误处理,生产代码中应补全):
#include <stdio.h>
#include <stdlib.h>
#include "json.gen.h"
#include "sstr.h"
int main() {
// ========================================
// 1. 创建游戏存档
// ========================================
struct GameSave save;
GameSave_init(&save);
save.save_name = sstr("英雄之旅-存档1");
save.version = 2;
save.play_time_seconds = 36000; // 10 小时
// 设置角色信息
save.player.name = sstr("亚瑟王");
save.player.class_name = sstr("圣骑士");
save.player.level = 42;
save.player.hp = 850;
save.player.max_hp = 1000;
save.player.mp = 320;
save.player.max_mp = 500;
save.player.attack = 156.5;
save.player.defense = 203.8;
// 设置位置
save.player.position.x = 1024.5;
save.player.position.y = -768.3;
// 设置背包物品(3 件)
save.player.inventory_len = 3;
save.player.inventory = (struct Item *)malloc(
sizeof(struct Item) * save.player.inventory_len);
// 物品 1:圣光之剑
Item_init(&save.player.inventory[0]);
save.player.inventory[0].name = sstr("圣光之剑");
save.player.inventory[0].type = sstr("weapon");
save.player.inventory[0].quantity = 1;
save.player.inventory[0].power = 285.0;
// 物品 2:生命药水
Item_init(&save.player.inventory[1]);
save.player.inventory[1].name = sstr("生命药水");
save.player.inventory[1].type = sstr("consumable");
save.player.inventory[1].quantity = 15;
save.player.inventory[1].power = 200.0;
// 物品 3:魔法盾牌
Item_init(&save.player.inventory[2]);
save.player.inventory[2].name = sstr("魔法盾牌");
save.player.inventory[2].type = sstr("armor");
save.player.inventory[2].quantity = 1;
save.player.inventory[2].power = 180.5;
// 设置成就
save.achievements_len = 3;
save.achievements = (sstr_t *)malloc(
sizeof(sstr_t) * save.achievements_len);
save.achievements[0] = sstr("初次冒险");
save.achievements[1] = sstr("屠龙勇士");
save.achievements[2] = sstr("百战不殆");
// ========================================
// 2. 序列化为 JSON(带缩进美化输出)
// ========================================
sstr_t json_out = sstr_new();
json_marshal_indent_GameSave(&save, 4, 0, json_out);
printf("=== 游戏存档 JSON ===\n%s\n", sstr_cstr(json_out));
// ========================================
// 3. 从 JSON 反序列化回结构体
// ========================================
struct GameSave loaded_save;
GameSave_init(&loaded_save);
int ret = json_unmarshal_GameSave(json_out, &loaded_save);
if (ret == 0) {
printf("\n=== 存档加载成功 ===\n");
printf("存档名: %s\n", sstr_cstr(loaded_save.save_name));
printf("角色: %s (Lv.%d %s)\n",
sstr_cstr(loaded_save.player.name),
loaded_save.player.level,
sstr_cstr(loaded_save.player.class_name));
printf("HP: %d/%d MP: %d/%d\n",
loaded_save.player.hp, loaded_save.player.max_hp,
loaded_save.player.mp, loaded_save.player.max_mp);
printf("位置: (%.1f, %.1f)\n",
loaded_save.player.position.x,
loaded_save.player.position.y);
printf("背包物品: %d 件\n", loaded_save.player.inventory_len);
int i;
for (i = 0; i < loaded_save.player.inventory_len; i++) {
printf(" - %s (%s) x%d 威力:%.1f\n",
sstr_cstr(loaded_save.player.inventory[i].name),
sstr_cstr(loaded_save.player.inventory[i].type),
loaded_save.player.inventory[i].quantity,
loaded_save.player.inventory[i].power);
}
printf("成就: ");
for (i = 0; i < loaded_save.achievements_len; i++) {
printf("[%s] ", sstr_cstr(loaded_save.achievements[i]));
}
printf("\n");
} else {
printf("存档加载失败!\n");
}
// ========================================
// 4. 清理资源
// ========================================
sstr_free(json_out);
GameSave_clear(&loaded_save);
GameSave_clear(&save);
return 0;
}7.4 编译与运行
# 生成代码
json-gen-c -in game_save.json-gen-c -out .
# 编译
gcc -o game_save main.c json.gen.c sstr.c -std=c11
# 运行
./game_save7.5 运行结果
程序输出的 JSON 存档数据如下(带 4 空格缩进的美观格式):
=== 游戏存档 JSON ===
{
"save_name": "英雄之旅-存档1",
"version": 2,
"play_time_seconds": 36000,
"player": {
"name": "亚瑟王",
"class_name": "圣骑士",
"level": 42,
"hp": 850,
"max_hp": 1000,
"mp": 320,
"max_mp": 500,
"attack": 156.500000,
"defense": 203.800000,
"position": {
"x": 1024.500000,
"y": -768.300000
},
"inventory": [
{
"name": "圣光之剑",
"type": "weapon",
"quantity": 1,
"power": 285.000000
},
{
"name": "生命药水",
"type": "consumable",
"quantity": 15,
"power": 200.000000
},
{
"name": "魔法盾牌",
"type": "armor",
"quantity": 1,
"power": 180.500000
}
]
},
"achievements": [
"初次冒险",
"屠龙勇士",
"百战不殆"
]
}
=== 存档加载成功 ===
存档名: 英雄之旅-存档1
角色: 亚瑟王 (Lv.42 圣骑士)
HP: 850/1000 MP: 320/500
位置: (1024.5, -768.3)
背包物品: 3 件
- 圣光之剑 (weapon) x1 威力:285.0
- 生命药水 (consumable) x15 威力:200.0
- 魔法盾牌 (armor) x1 威力:180.5
成就: [初次冒险] [屠龙勇士] [百战不殆]
可以看到,我们没有手写任何解析或序列化逻辑——只需定义数据结构,常规的 JSON 序列化/反序列化代码就由 json-gen-c 自动生成了。
8. 与传统方式的代码量对比
下面对比一下,实现同样的”游戏存档序列化/反序列化”功能,使用 cJSON 手写需要多少代码。
以下对比基于同一 RPG 存档示例的手写代码量估算,不含自动生成的代码和测试代码:
8.1 使用 json-gen-c
| 内容 | 代码行数 |
|---|---|
结构体定义(.json-gen-c) |
~35 行 |
| 主程序(创建数据 + 序列化 + 反序列化 + 打印) | ~100 行 |
| 手写 JSON 处理代码 | 0 行 |
| 总计(需要手写的代码) | ~135 行 |
8.2 使用 cJSON 手写
| 内容 | 代码行数 |
|---|---|
结构体定义(.h 文件中) |
~35 行 |
| 序列化函数(struct -> cJSON -> string) | ~80 行 |
| 反序列化函数(string -> cJSON -> struct) | ~120 行 |
init / clear 函数 |
~40 行 |
| 错误处理代码 | ~30 行 |
| 主程序 | ~100 行 |
| 总计(需要手写的代码) | ~405 行 |
直观对比:
json-gen-c 将手动编写代码量减少了约 67%,并显著降低了手写序列化逻辑中常见的拼写错误、类型不匹配、遗漏字段等 bug 风险。
但代码行数只是表面收益。真正更重要的差异体现在结构演进时的维护成本:如果明天你的结构体需要新增一个字段,json-gen-c 只需在定义文件中加一行然后重新生成——改动恰好一处。而 cJSON 的方案则需要同步修改序列化函数、反序列化函数、init 函数和 clear 函数——四处修改,四处可能出错。随着项目迭代,这种”改一处 vs 改四处”的差距会被不断放大,这才是代码生成方案最核心的工程价值。
9. 进阶:数组批量操作
json-gen-c 还支持结构体数组级别的序列化和反序列化。例如,保存多个角色存档:
// 批量序列化
struct GameSave saves[3];
// ... 分别初始化 saves[0], saves[1], saves[2] ...
sstr_t json_out = sstr_new();
json_marshal_array_GameSave(saves, 3, json_out);
printf("%s\n", sstr_cstr(json_out));
// 批量反序列化
struct GameSave *loaded = NULL;
int count = 0;
json_unmarshal_array_GameSave(json_out, &loaded, &count);
printf("已加载 %d 个存档\n", count);
int i;
for (i = 0; i < count; i++) {
printf("存档 %d: %s\n", i + 1, sstr_cstr(loaded[i].save_name));
GameSave_clear(&loaded[i]);
}
free(loaded);
sstr_free(json_out);这在实现“存档列表”、“排行榜数据”等功能时非常实用。
10. 总结
JSON 的地位
尽管有着不支持注释、缺乏日期类型、数字精度问题等缺陷,JSON 凭借其极致的简洁、广泛的跨语言支持和良好的人类可读性,仍然是当今互联网上最通用的数据交换格式之一。在可见的主流 Web 场景中,JSON 仍将长期占据主导地位。
选择合适的格式
不同场景有不同的最佳选择: - Web API – JSON - 高性能通信 – Protobuf / MessagePack - 配置文件 – TOML(简单)/ YAML(复杂) - 企业集成 – XML - 数据库存储 – BSON / JSON
json-gen-c 的价值
对于 C 程序员来说,json-gen-c 提供了一条优雅的道路:
- 定义一次结构体,自动获得完整的 JSON 能力
- 零运行时反射开销,生成代码的性能通常接近手写水平
- 线程安全,适合高并发服务端程序
- 生成的代码版权归用户,GPL-3.0 许可不感染生成物
- 自带
sstr字符串库,告别char*的内存管理噩梦
json-gen-c 不是要把 C 变成动态语言,而是把那些重复、机械、易错的 JSON 样板代码提前转移到代码生成阶段——让你在写 C 的时候,只需要关心数据本身。
如果你正在用 C 语言开发需要处理 JSON 的项目——无论是嵌入式设备上的配置解析、游戏引擎中的数据存储,还是高性能服务器的协议处理——不妨试试 json-gen-c。
项目资源:
- GitHub: https://github.com/zltl/json-gen-c
- API 文档: https://zltl.github.io/json-gen-c/
- sstr 字符串库文档: https://zltl.github.io/json-gen-c/sstr_8h.html