3. 配置文件格式选型:INI、JSON、YAML、XML、TOML 的对比与选型策略

做嵌入式这么多年,我见过太多因为配置文件格式选错而翻车的项目。有的团队图省事全用 INI,结果嵌套结构一多就崩了。有的盲目上 XML,解析器占掉一半 Flash。说白了,选配置文件格式跟选螺丝刀一样——得看你要拧什么螺丝。

这一章,我把主流的五种格式掰开揉碎讲清楚。你以后做选型时,心里就有底了。

3.1 五种格式的「基因」差异

先看一张总览表。我习惯把格式分成两派:人类友好型和机器友好型。INI 和 TOML 偏前者,JSON 和 XML 偏后者,YAML 在中间摇摆。

特性 INI JSON YAML XML TOML
诞生年份 1970s 2001 2001 1996 2013
核心哲学 极简 数据交换 人类可读 通用标记 无歧义
类型系统 纯文本 强类型 自动推断 纯文本 强类型
嵌套支持 差(需变通) 好 好 好 好
注释支持 ✅ ❌ ✅ ✅ ✅
解析器体积 极小 小 中等 大 中等
典型场景 简单配置 Web API DevOps 配置 文档/互操作 复杂配置

我的经验:在资源受限的 MCU 上,解析器体积是硬门槛。JSON 的解析器可以做到 2KB 以内,而 XML 的解析器动辄 20KB+。你想想看,Flash 总共才 64KB 的设备,你敢上 XML 吗?

3.2 逐个拆解:优点、缺点、坑

3.2.1 INI —— 老当益壮,但别贪心

INI 是最古老的格式。它的结构就三样:节(Section)、键(Key)、值(Value)。

[uart]
baudrate = 115200
parity = none
stop_bits = 1

[gpio]
pin_led = 12
pin_button = 14

优点:简单到不能再简单。手写、手读都毫无压力。解析器几十行 C 代码就能搞定。

缺点:没有类型。所有值都是字符串。你想表示布尔值?得自己约定 "true"/"false"。嵌套?不存在的。

我曾经踩过的坑:有个项目用 INI 存 Wi-Fi 配置列表,结果发现 INI 不支持数组。最后只能用 ssid_1、ssid_2 这种命名方式,代码里写循环去拼字符串。那叫一个难受。后来我定了个规矩:超过 3 层嵌套或需要列表的,别用 INI。

3.2.2 JSON —— 互联网的通用语

JSON 是 JavaScript 的儿子,但早已独立。它最大的优势是生态——几乎所有语言都有原生支持。

{
  "device": {
    "name": "sensor_node_01",
    "firmware": "v2.3.1",
    "sensors": [
      {"type": "temperature", "pin": 4},
      {"type": "humidity", "pin": 5}
    ]
  }
}

优点:类型明确(字符串、数字、布尔、数组、对象)。解析速度快。序列化/反序列化库遍地都是。

缺点:不支持注释。这是个硬伤。你没法在配置文件里写「这个参数别乱改」之类的说明。另外,JSON 对手写不友好——少个逗号就解析失败,错误信息还经常看不懂。

我的建议:如果配置文件需要被程序频繁读写,或者要跟云端交互,选 JSON 准没错。但如果是运维人员手写维护的配置,我建议你加一层校验工具,或者干脆换 TOML。

3.2.3 YAML —— 人类可读的「陷阱」

YAML 的卖点是「人类可读」。它用缩进表示层级,看起来确实清爽。

device:
  name: sensor_node_01
  firmware: v2.3.1
  sensors:
    - type: temperature
      pin: 4
    - type: humidity
      pin: 5

优点:可读性确实好。支持注释。支持多文档(用 --- 分隔)。

缺点:缩进敏感。一个空格不对,整个文件就废了。而且 YAML 的类型推断有时会坑人——yes 会被解析成布尔值 true,0123 会被当成八进制数。

我曾经遇到的事故:有次设备固件升级后,某个参数从 version: 1.0 变成了 version: 1.0.0。YAML 把 1.0 解析成浮点数,1.0.0 解析成字符串。结果版本比较逻辑直接崩了。所以我的建议是:在嵌入式场景里,YAML 只适合做「人看」的模板,不适合做「机器读」的正式配置。

3.2.4 XML —— 重型武器,慎用

XML 是文档界的瑞士军刀。它强大,但也臃肿。

<device>
  <name>sensor_node_01</name>
  <firmware>v2.3.1</firmware>
  <sensors>
    <sensor type="temperature" pin="4"/>
    <sensor type="humidity" pin="5"/>
  </sensors>
</device>

优点:有 Schema 校验(XSD)。支持命名空间。有 XPath/XSLT 等配套工具。适合复杂文档结构。

缺点:冗长。解析慢。解析器体积大。在嵌入式领域,除非是工业协议(比如 OPC-UA)强制要求,否则我基本不用。

我的原则:XML 只用在两种场景:一是行业标准强制要求(比如医疗设备的数据交换),二是配置本身需要 Schema 校验来保证正确性。其他情况,一律不考虑。

3.2.5 TOML —— 现代配置的「最优解」

TOML 是后来者,但它的设计目标很明确:做 INI 的现代替代品。它保留了 INI 的直观,又加入了类型和嵌套。

[device]
name = "sensor_node_01"
firmware = "v2.3.1"

[[device.sensors]]
type = "temperature"
pin = 4

[[device.sensors]]
type = "humidity"
pin = 5

优点:类型明确。支持注释。支持嵌套表和数组。无歧义——同样的数据,TOML 只有一种写法。

缺点:生态不如 JSON 成熟。某些语言(比如 C)的解析库选择较少。数组的语法([[...]])初次接触会觉得奇怪。

我现在的习惯:新项目做配置管理,我首选 TOML。它几乎解决了 INI 的所有痛点,又不像 YAML 那样有歧义。唯一要注意的是:在资源极度受限的 MCU 上,先确认一下有没有现成的 C 语言解析库。没有的话,自己写一个也不难——TOML 的语法规则很清晰。

3.3 选型决策流程

说了这么多,到底怎么选?我画了一张决策图,你照着走就行。

配置文件格式选型决策流程 开始选型 MCU Flash < 64KB 或 RAM < 8KB? 是 INI(极简配置) 否 需要手写维护 + 注释? 是 嵌套层级 > 2 或需要列表? 是 TOML(现代配置) 否 INI(简单配置) 否 需要 Schema 校验或行业标准? 是 XML(重型配置) 否 JSON(数据交换) 说明 YAML 在嵌入式场景 中不推荐作为正式配置

3.4 实战选型清单

最后,我整理了一份清单。你每次做选型时,对着打勾就行。

  1. 目标平台是什么?—— MCU 还是 Linux 网关?MCU 优先 INI 或 JSON。
  2. 谁在维护配置?—— 运维人员手写?选 TOML 或 INI。程序自动生成?选 JSON。
  3. 配置有多复杂?—— 纯键值对?INI 够用。有嵌套和列表?TOML 或 JSON。
  4. 需要注释吗?—— 需要的话,排除 JSON。
  5. 有行业标准要求吗?—— 有的话,只能 XML。
  6. 解析器资源够吗?—— Flash 和 RAM 紧张的话,别碰 XML 和 YAML。

我的最终建议:没有银弹。但如果你让我推荐一个「万金油」方案,我会说:新项目默认用 TOML,除非有明确的理由换别的。它平衡了可读性、类型安全、解析器体积和生态成熟度。我在最近三个项目里都这么用,还没翻过车。