Featured image of post 什么是Toml

什么是Toml

TOML(Tom's Obvious, Minimal Language)是一种语义明确、易于阅读和编写的配置文件格式。其设计目标是成为比 JSON、YAML、INI 更直观、无歧义的配置语言。

TOML 文件在 C# 中的全面指南

适用于 .NET 开发者:从基础概念到实战应用,掌握现代配置文件格式 TOML 在 C# 项目中的使用方法。


一、什么是 TOML?

TOMLTom’s Obvious, Minimal Language)是一种语义明确、易于阅读和编写的配置文件格式,由 GitHub 联合创始人 Tom Preston-Werner 提出。其设计目标是成为比 JSON、YAML、INI 更直观、无歧义的配置语言。

核心特点

  • 人类可读:语法简洁,结构清晰
  • 强类型支持:原生支持字符串、整数、布尔、日期、数组、嵌套表等
  • 标准化:有明确规范(toml.io),跨语言兼容
  • 支持注释:使用 # 添加说明
  • 层级结构:通过 [section][section.subsection] 实现嵌套

示例 TOML 文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 应用基本信息
title = "My C# Application"
version = "1.0.0"

[server]
host = "localhost"
port = 8080
ssl_enabled = true

[database]
server = "192.168.1.10"
port = 5432

[database.credentials]
username = "admin"
password = "s3cr3t"

二、TOML 与其他配置格式对比

特性 TOML INI JSON YAML
人类可读性 ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐
支持注释 ✅(# ✅(#;,不标准) ✅(#
数据类型 原生支持多种类型 仅字符串 支持基本类型 支持但易出错
嵌套结构 ✅(通过表) ❌(需命名约定) ✅(对象) ✅(缩进)
标准规范 ✅(严格) ❌(各实现不一) ✅(但复杂)
缩进敏感 ✅(易错)
适合手写 ✅✅✅ ✅✅ ✅(但需小心缩进)

💡 结论:TOML 是 INI 的现代化升级版,兼具可读性与结构表达能力,特别适合应用程序配置文件。


三、为什么在 C# 项目中使用 TOML?

  • 现代项目趋势:Rust(Cargo.toml)、Python(pyproject.toml)、Deno 等广泛采用
  • 优于传统 appsettings.json:支持注释,结构更清晰,避免 JSON 的“无注释”痛点
  • 比 XML 简洁:无需闭合标签,无命名空间复杂性
  • 比 YAML 安全:无缩进陷阱,无类型自动推断风险

📌 典型应用场景:

  • 应用程序主配置文件(替代 appsettings.json
  • 插件/模块配置
  • 构建脚本参数(如自定义构建工具)
  • 游戏或桌面应用的用户设置

四、C# 中读写 TOML:使用 Tomlyn 库

.NET 官方未内置 TOML 支持,但社区库 Tomlyn 是目前最成熟、轻量、高性能的选择。

1. 安装

通过 NuGet 安装:

1
dotnet add package Tomlyn

或在 .csproj 中添加:

1
<PackageReference Include="Tomlyn" Version="0.15.0" />

✅ 支持 .NET Standard 2.0+,兼容 .NET Core、.NET 5/6/7/8 及 .NET Framework。


2. 加载 TOML 文件

方式一:强类型反序列化(推荐)

适用于结构固定的配置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using Tomlyn;

public class AppConfig
{
    public string Title { get; set; } = "";
    public ServerConfig Server { get; set; } = new();
    public DatabaseConfig Database { get; set; } = new();
}

public class ServerConfig
{
    public string Host { get; set; } = "localhost";
    public int Port { get; set; } = 8080;
    public bool SslEnabled { get; set; }
}

public class DatabaseConfig
{
    public string Server { get; set; } = "";
    public int Port { get; set; }
    public DbCredentials Credentials { get; set; } = new();
}

public class DbCredentials
{
    public string Username { get; set; } = "";
    public string Password { get; set; } = "";
}

加载代码:

1
2
3
4
string toml = File.ReadAllText("app.toml");
var config = Toml.ToModel<AppConfig>(toml);

Console.WriteLine($"Server: {config.Server.Host}:{config.Server.Port}");

✅ 自动类型转换:int、bool、嵌套对象等无需手动解析。

方式二:动态解析(灵活但需手动处理)

适用于结构未知或临时解析。

1
2
3
4
5
6
using Tomlyn.Model;

var model = Toml.ToModel(File.ReadAllText("config.toml"));
var server = (TomlTable)model["server"];
string host = (string)server["host"];
int port = (int)server["port"];

⚠️ 注意:需显式类型转换,嵌套需递归处理。


3. 保存 TOML 文件

将 C# 对象序列化为 TOML 文本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
var config = new AppConfig
{
    Title = "My App",
    Server = new() { Host = "0.0.0.0", Port = 9000, SslEnabled = false },
    Database = new()
    {
        Server = "db.example.com",
        Port = 5432,
        Credentials = new() { Username = "user", Password = "pass" }
    }
};

string tomlText = Toml.FromModel(config);
File.WriteAllText("app.toml", tomlText);

输出示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
title = "My App"

[server]
host = "0.0.0.0"
port = 9000
ssl_enabled = false

[database]
server = "db.example.com"
port = 5432

[database.credentials]
username = "user"
password = "pass"

4. 高级特性

自定义字段名映射

当 TOML 键名与 C# 属性名不一致时,使用 [TomlProperty]

1
2
3
4
5
6
7
8
public class ServerConfig
{
    [TomlProperty("bind_address")]
    public string Host { get; set; } = "";

    [TomlProperty("use_tls")]
    public bool SslEnabled { get; set; }
}

对应 TOML:

1
2
3
[server]
bind_address = "127.0.0.1"
use_tls = true

数组支持

TOML:

1
2
allowed_ips = ["192.168.1.1", "10.0.0.5"]
ports = [8080, 8081, 8082]

C#:

1
2
public List<string> AllowedIps { get; set; } = new();
public int[] Ports { get; set; } = Array.Empty<int>();

日期时间

TOML 支持 RFC 3339 日期:

1
created_at = 2025-10-30T14:30:00Z

C#:

1
public DateTime CreatedAt { get; set; }

五、完整工具类封装(推荐复用)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using Tomlyn;
using System.IO;

/// <summary>
/// TOML 配置文件读写工具类
/// </summary>
public static class TomlConfigHelper
{
    /// <summary>
    /// 从文件加载 TOML 配置(强类型)
    /// </summary>
    public static T Load<T>(string filePath) where T : new()
    {
        if (!File.Exists(filePath))
            throw new FileNotFoundException($"TOML 配置文件不存在: {filePath}");

        var content = File.ReadAllText(filePath);
        return Toml.ToModel<T>(content);
    }

    /// <summary>
    /// 保存配置对象到 TOML 文件
    /// </summary>
    public static void Save<T>(string filePath, T config)
    {
        var content = Toml.FromModel(config);
        Directory.CreateDirectory(Path.GetDirectoryName(filePath) ?? ".");
        File.WriteAllText(filePath, content);
    }
}

使用示例:

1
2
3
4
5
6
7
8
9
// 首次运行:创建默认配置
if (!File.Exists("config.toml"))
{
    var defaultConfig = new AppConfig { Title = "My App" };
    TomlConfigHelper.Save("config.toml", defaultConfig);
}

// 加载配置
var config = TomlConfigHelper.Load<AppConfig>("config.toml");

六、常见问题与注意事项

问题 解决方案
属性未赋值 确保属性为 public 且有 set 访问器
类型转换异常 检查 TOML 值类型是否匹配(如 "8080" 是字符串,应写为 8080
嵌套结构不匹配 C# 类的嵌套层级必须与 TOML 表结构一致
中文乱码 保存 TOML 文件时使用 UTF-8 编码(File.WriteAllText 默认 UTF-8)
注释丢失 Tomlyn 不保留注释(符合 TOML 规范),如需保留注释需用其他方案

⚠️ TOML 规范要求:键名区分大小写,字符串建议用双引号(虽然部分值可无引号,但为安全起见推荐显式使用)。


七、与其他 .NET 配置系统的集成(可选)

虽然 TOML 不能直接用于 Microsoft.Extensions.Configuration(如 ASP.NET Core 的 IConfiguration),但可通过自定义 ConfigurationProvider 实现集成。

🔧 如有需要,可基于 Tomlyn 实现 TomlConfigurationProvider,将 TOML 文件纳入标准配置管道。


八、总结

优势 说明
简洁直观 比 JSON 更易读,比 YAML 更安全
类型安全 原生支持多种数据类型,减少解析错误
现代标准 被 Rust、Python 等主流生态广泛采用
C# 支持完善 Tomlyn 库成熟、轻量、高性能

✅ 推荐在以下场景使用 TOML:

  • 桌面应用、控制台工具、游戏等需要用户可编辑配置的项目
  • 替代 appsettings.json 以支持注释和更清晰结构
  • 构建系统、CI/CD 脚本中的参数配置

附录:参考资料


📝 作者建议:对于新项目,优先考虑 TOML 作为配置格式;对于现有项目,可在非核心模块中逐步引入,体验其简洁与强大。


Built with Hugo
Theme Stack designed by Jimmy