YamlDotNet 是 C# 读 YAML 的事实标准,支持嵌套、锚点、多文档等特性;需注意大小写匹配、类型转换、复用 Deserializer 实例及正确处理 alias/tag。

用 YamlDotNet 读取 YAML 文件最稳
直接上结论:C# 读 YAML,YamlDotNet 是当前事实标准,别折腾 SharpYaml 或手写正则。它支持 .NET Standard 2.0+,能处理嵌套、锚点、标签、多文档,而且异常信息够具体——出错时你能看清是哪行 YAML 格式不对,而不是抛个 YamlException 就完事。
安装包命令:dotnet add package YamlDotNet。注意别装错成 YamlDotNet.Serialization 单独包(它只是子模块,主包已含)。
常见错误现象:YamlException: (Line: X, Col: Y) Exception during deserialization,大概率是字段名大小写不匹配或类型强制转换失败(比如 YAML 里写 timeout: 30s,但 C# 类型定义为 int Timeout)。
- 必须确保目标类的属性有 public getter/setter,且命名与 YAML key 严格对应(默认区分大小写)
- 若 YAML key 是
api_url,C# 属性建议用[YamlMember(Alias = "api_url")] - 避免用
dynamic解析深层结构——类型丢失后,后续访问容易崩在运行时,不如定义明确的 DTO 类
遇到日期、布尔、数字类型解析失败怎么办
YAML 的松散语法会让 YamlDotNet 在类型推断上“自作主张”:比如 enabled: yes 被当成字符串而非 bool;created: 2023-10-05 可能被当字符串,除非你显式告诉它这是 DateTime。
根本原因是 Deserializer 默认只注册了基础类型映射,没开自动类型猜测(ignoreUnmatched 关闭时更明显)。
- 手动注册类型转换器:比如用
new SerializerBuilder().WithNamingConvention(new CamelCaseNamingConvention())配合.WithTypeConverter(new DateTimeConverter()) - 布尔值推荐统一用
true/false,别混用yes/no、on/off—— 后者需要额外注册BooleanConverter - 数字字段如果可能含单位(如
timeout: 30s),别指望自动转TimeSpan,得自己写IYamlTypeConverter
读取多文档 YAML(--- 分隔)要主动迭代
YAML 支持一个文件里多个文档,用 --- 分隔,但 Deserialize 默认只取第一个。想全读?不能偷懒,得用 IDeserializer + IParser 手动遍历。
典型场景:Kubernetes 风格配置、CI/CD 模板、微服务多实例配置合并。
- 用
var parser = new Parser(new StringReader(yamlContent))初始化解析器 - 循环调用
parser.MoveNext(),每次进if (parser.Current is DocumentStart)后再deserializer.Deserialize(parser) - 别用
Deserialize试图“自动展开”,它只会把整个文档当数组第一项>
性能敏感时别每次 new Deserializer
反复读取配置(比如 Web API 每次请求都加载 YAML)时,new Deserializer() 开销不小——它内部会重建类型映射缓存和命名策略。实测千次解析,复用实例比每次都 new 快 3–5 倍。
不是所有场景都要单例,但至少做成静态只读字段或 DI 容器注册为 Singleton。
- 推荐写法:
private static readonly IDeserializer Deserializer = new DeserializerBuilder().Build(); - 如果用了自定义 converter 或 naming convention,确保它们线程安全(比如
CamelCaseNamingConvention是安全的,但你自己写的 stateful converter 就未必) - 注意:
Deserializer不是完全无状态——如果你在反序列化中抛异常,它可能残留部分解析上下文,但不影响下一次调用
最容易被忽略的是 alias(& 和 *)和 tag(!!str / !!int)的支持,默认开启,但一旦你在 YAML 里用了 *ref 引用,而 DTO 类没设引用跟踪(WithEventEmitter(...)),就会报循环引用异常——这时候得开 IgnoreUnmatchedProperties 或换用 DeserializerBuilder().WithObjectFactory(...) 控制实例创建逻辑。











