C#常用类库-详解YamlDotNet

C#常用类库-详解YamlDotNet

在C#开发中,数据序列化与反序列化是高频需求,广泛应用于配置文件、数据传输、日志存储等场景。YAML作为简洁易读的非标记语言,相比XML繁琐、JSON紧凑的特点,更适合作为配置文件和高可读性的数据交换格式。而YamlDotNet作为C#生态最主流的YAML处理类库,开源免费、功能强大,是处理YAML数据的首选工具。本文聚焦实战,从核心定位、环境搭建、基础用法到进阶技巧,帮你快速掌握YamlDotNet全流程用法。

一、YamlDotNet 核心定位与优势

YamlDotNet是基于MIT许可证的开源C#类库,核心功能是实现.NET对象与YAML格式的双向转换(序列化/反序列化),底层封装了YAML语法解析器与生成器,提供简洁API,支持自定义规则,适配复杂业务场景。

1. 核心优势

  • 开源免费:可自由用于商业项目,源码可定制,社区活跃,适配.NET全框架(.NET Framework 4.5+、.NET Core 2.0+、.NET 5+)。

  • 功能完整:支持基本类型、自定义类、集合、泛型、继承类的序列化/反序列化,兼容YAML 1.1和1.2规范。

  • 灵活可扩展:支持自定义序列化规则、属性映射、忽略指定属性、保留注释,适配复杂业务场景。

  • 兼容性强:跨平台支持(Windows、Linux、macOS),无第三方依赖,集成简单。

  • 性能优异:解析速度快、内存占用低,支持大数据量YAML文档处理,满足企业级需求。

2. 与其他序列化类库选型对比

类库 核心优势 不足 选型建议
YamlDotNet 专注YAML,易用性强、扩展灵活,支持注释和复杂类型 序列化速度略逊于Newtonsoft.Json,不支持JSON 配置文件、高可读性数据交换首选
Newtonsoft.Json 速度快、功能丰富、生态完善,专注JSON 不支持YAML,配置文件可读性差 JSON数据传输、接口交互场景
System.Xml.Serialization 内置无依赖,适配传统XML配置 语法繁琐、扩展性低,不支持YAML 旧项目XML配置兼容场景
SharpYaml 轻量、解析快,支持YAML 1.2 社区活跃低、高级功能不完善 简单YAML解析场景,不推荐复杂项目

3. 核心应用场景

  • 配置文件:替代XML、JSON,作为appsettings.yaml等配置文件,便于人工编辑。

  • 数据交换:微服务、跨语言项目中,作为高可读性的数据交换格式。

  • 日志存储:将复杂日志对象序列化为YAML,便于分析查看。

  • 文档生成:生成YAML格式接口文档、K8s配置文件等。

  • 数据导入导出:实现系统数据的备份与恢复。

二、环境搭建:快速集成YamlDotNet

YamlDotNet为第三方类库,需通过NuGet安装,搭建流程简单,适配所有.NET版本。

1. 安装方式(3种任选)

方式1:NuGet界面安装

Visual Studio → 右键项目 → 管理NuGet程序包 → 搜索"YamlDotNet" → 安装最新稳定版。

方式2:Package Manager Console命令
powershell 复制代码
Install-Package YamlDotNet -Version 13.7.1 # 可替换为最新版本
方式3:手动修改.csproj文件
xml 复制代码
<ItemGroup>
  <PackageReference Include="YamlDotNet" Version="13.7.1" />
</ItemGroup>

2. 引入命名空间

csharp 复制代码
using YamlDotNet;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

3. 环境验证

编写简单代码,验证序列化与反序列化功能是否正常:

csharp 复制代码
// 1. 定义测试类
public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
    public List<string> Hobbies { get; set; }
}

// 2. 序列化(对象→YAML)
var user = new User { Name = "ZhangSan", Age = 25, Hobbies = new List<string> { "Coding", "Reading" } };
var serializer = new SerializerBuilder()
    .WithNamingConvention(CamelCaseNamingConvention.Instance)
    .Build();
string yaml = serializer.Serialize(user);
Console.WriteLine("序列化结果:\n" + yaml);

// 3. 反序列化(YAML→对象)
var deserializer = new DeserializerBuilder()
    .WithNamingConvention(CamelCaseNamingConvention.Instance)
    .IgnoreUnmatchedProperties()
    .Build();
User deserializedUser = deserializer.Deserialize<User>(yaml);
Console.WriteLine($"反序列化结果:Name={deserializedUser.Name}, Age={deserializedUser.Age}");

运行程序,正常输出结果即说明环境搭建成功。

三、基础实战:核心用法(序列化+反序列化)

YamlDotNet的核心的是序列化与反序列化,以下覆盖常用场景,代码可直接复制使用。

1. 通用配置

序列化器与反序列化器的常用配置,统一配置可提升代码复用性:

csharp 复制代码
// 序列化器配置
var serializer = new SerializerBuilder()
    .WithNamingConvention(CamelCaseNamingConvention.Instance) // 驼峰命名
    .Indent(2) // 缩进2个空格
    .WithCommentHandling(CommentHandling.IncludeComments) // 保留注释
    .Build();

// 反序列化器配置
var deserializer = new DeserializerBuilder()
    .WithNamingConvention(CamelCaseNamingConvention.Instance)
    .IgnoreUnmatchedProperties() // 忽略未知属性,避免报错
    .AllowNullValues() // 允许空值
    .Build();

2. 基本类型与集合

支持string、int、DateTime等基本类型,以及List、Dictionary等集合:

csharp 复制代码
var data = new
{
    Name = "LiSi",
    Age = 30,
    BirthDate = new DateTime(1993, 10, 1),
    Tags = new string[] { "C#", "YamlDotNet" },
    Scores = new Dictionary<string, int> { { "Math", 90 }, { "English", 85 } }
};

// 序列化
string yaml = serializer.Serialize(data);

// 反序列化(dynamic类型,无需定义实体类)
dynamic deserialized = deserializer.Deserialize<dynamic>(yaml);
Console.WriteLine($"Math Score: {deserialized.scores.math}");

3. 自定义类(重点)

支持自定义类、继承类、嵌套类的序列化与反序列化:

csharp 复制代码
// 嵌套类
public class Address { public string Province { get; set; } public string City { get; set; } }
// 父类
public class Person { public string Name { get; set; } public int Age { get; set; } }
// 子类
public class Student : Person
{
    public string StudentId { get; set; }
    public Address Address { get; set; }
    public List<string> Courses { get; set; }
}

// 序列化
var student = new Student
{
    Name = "WangWu",
    Age = 20,
    StudentId = "2023001",
    Address = new Address { Province = "Guangdong", City = "Shenzhen" },
    Courses = new List<string> { "C#", "Database" }
};
string studentYaml = serializer.Serialize(student);

// 反序列化
Student deserializedStudent = deserializer.Deserialize<Student>(studentYaml);

4. 特性用法(自定义规则)

通过特性自定义单个属性的序列化规则,灵活适配需求:

csharp 复制代码
using YamlDotNet.Serialization.Attributes;

public class Product
{
    [YamlAlias("product_id")] // 别名,YAML中显示为product_id
    public string Id { get; set; }

    [YamlIgnore] // 忽略该属性,不序列化/反序列化
    public string Secret { get; set; }

    [YamlComment("产品名称(必填)")] // 添加注释
    public string Name { get; set; }

    [YamlMember(Order = 1)] // 指定序列化顺序
    public decimal Price { get; set; }
}

四、进阶实战:企业级优化技巧

基础用法可满足简单场景,企业级项目需解决复杂类型、性能、稳定性等问题,以下是核心技巧。

1. 自定义类型转换器

针对DateTime、Enum等复杂类型,自定义转换规则(以DateTime格式化为例):

csharp 复制代码
// 实现自定义转换器
public class CustomDateTimeConverter : IYamlTypeConverter
{
    public bool Accepts(Type type) => type == typeof(DateTime) || type == typeof(DateTime?);

    // 反序列化:字符串→DateTime
    public object ReadYaml(IParser parser, Type type)
    {
        var value = parser.Consume<Scalar>().Value;
        return DateTime.ParseExact(value, "yyyy-MM-dd HH:mm:ss", null);
    }

    // 序列化:DateTime→字符串
    public void WriteYaml(IEmitter emitter, object value, Type type)
    {
        emitter.Emit(new Scalar(((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss")));
    }
}

// 注册并使用
var serializer = new SerializerBuilder()
    .WithTypeConverter(new CustomDateTimeConverter())
    .Build();

2. YAML文件读写(实战常用)

封装工具方法,实现对象与YAML文件的双向读写:

csharp 复制代码
/// <summary>
/// 序列化对象到YAML文件
/// </summary>
public void SerializeToFile(object obj, string filePath)
{
    using (var writer = new StreamWriter(filePath, false, Encoding.UTF8))
    {
        serializer.Serialize(writer, obj);
    }
}

/// <summary>
/// 从YAML文件反序列化到对象
/// </summary>
public T DeserializeFromFile<T>(string filePath)
{
    if (!File.Exists(filePath)) throw new FileNotFoundException("YAML文件不存在", filePath);
    using (var reader = new StreamReader(filePath, Encoding.UTF8))
    {
        return deserializer.Deserialize<T>(reader);
    }
}

3. 异常处理与性能优化

(1)异常处理

完善异常处理,避免程序崩溃,提升稳定性:

csharp 复制代码
public T DeserializeWithExceptionHandling<T>(string yaml)
{
    try
    {
        return deserializer.Deserialize<T>(yaml);
    }
    catch (YamlException ex)
    {
        Console.WriteLine($"YAML语法错误:{ex.Message},行号:{ex.Start.Line}");
        return default;
    }
    catch (InvalidCastException ex)
    {
        Console.WriteLine($"类型转换错误:{ex.Message}");
        return default;
    }
    catch (Exception ex)
    {
        Console.WriteLine($"反序列化失败:{ex.Message}");
        return default;
    }
}
(2)性能优化(大数据量场景)
  • 使用Stream读写,减少内存占用;

  • 复用序列化器/反序列化器实例(单例模式);

  • 禁用不必要的功能(如注释处理);

  • 避免使用dynamic类型,优先使用具体类型。

五、避坑指南与最佳实践

1. 常见坑与解决方案

  • 坑1:YAML语法错误:缩进用空格(不支持Tab),冒号、横杠后必须加空格;用在线工具校验语法。

  • 坑2:命名不一致:序列化与反序列化配置相同的命名约定,或用[YamlAlias]指定别名。

  • 坑3:复杂类型格式不符:实现自定义类型转换器,或使用内置配置。

  • 坑4:未知属性报错:配置IgnoreUnmatchedProperties()忽略未知属性。

  • 坑5:大数据量性能差:用Stream、复用实例、禁用注释处理。

2. 最佳实践

  • 统一命名约定,保持序列化与反序列化配置一致;

  • 复用序列化器/反序列化器实例,提升性能;

  • 敏感字段用[YamlIgnore]忽略,关键属性加注释;

  • 配置文件场景保留注释,便于维护;

  • 序列化后校验YAML语法,避免反序列化失败。

六、企业级实战案例:配置文件读写工具

结合前文知识点,实现可直接复用的YAML配置读写工具,适配项目全局配置场景。

1. 项目结构

text 复制代码
YamlConfigDemo/
├─ Config/
│  ├─ AppConfig.cs(配置实体)
│  └─ YamlConfigHelper.cs(工具类)
├─ appsettings.yaml(配置文件)
└─ Program.cs(测试入口)

2. 核心代码

(1)配置实体类(AppConfig.cs)
csharp 复制代码
using YamlDotNet.Serialization.Attributes;

namespace YamlConfigDemo.Config
{
    public class AppConfig
    {
        [YamlComment("应用名称(必填)")]
        public string AppName { get; set; }

        [YamlComment("应用版本")]
        public string Version { get; set; }

        [YamlComment("数据库配置")]
        public DatabaseConfig Database { get; set; }

        [YamlComment("服务配置")]
        public ServiceConfig Service { get; set; }
    }

    public class DatabaseConfig
    {
        [YamlAlias("connection_string")]
        public string ConnectionString { get; set; }

        [YamlAlias("timeout")]
        public int Timeout { get; set; } = 30;
    }

    public class ServiceConfig
    {
        [YamlAlias("base_url")]
        public string BaseUrl { get; set; }

        [YamlAlias("timeout")]
        public int Timeout { get; set; } = 5000;
    }
}
(2)配置读写工具类(YamlConfigHelper.cs)
csharp 复制代码
using System.IO;
using System.Text;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

namespace YamlConfigDemo.Config
{
    public static class YamlConfigHelper
    {
        // 单例复用实例
        private static readonly Serializer _serializer;
        private static readonly Deserializer _deserializer;

        static YamlConfigHelper()
        {
            _serializer = new SerializerBuilder()
                .WithNamingConvention(CamelCaseNamingConvention.Instance)
                .WithCommentHandling(CommentHandling.IncludeComments)
                .Indent(4)
                .Build();

            _deserializer = new DeserializerBuilder()
                .WithNamingConvention(CamelCaseNamingConvention.Instance)
                .IgnoreUnmatchedProperties()
                .AllowNullValues()
                .Build();
        }

        // 读取配置
        public static T ReadConfig<T>(string filePath)
        {
            if (!File.Exists(filePath)) throw new FileNotFoundException("配置文件不存在", filePath);
            using (var reader = new StreamReader(filePath, Encoding.UTF8))
            {
                return _deserializer.Deserialize<T>(reader);
            }
        }

        // 写入配置
        public static void WriteConfig(object config, string filePath)
        {
            using (var writer = new StreamWriter(filePath, false, Encoding.UTF8))
            {
                _serializer.Serialize(writer, config);
            }
        }
    }
}
(3)测试使用(Program.cs)
csharp 复制代码
using YamlConfigDemo.Config;

// 写入配置
var config = new AppConfig
{
    AppName = "YamlDotNetDemo",
    Version = "1.0.0",
    Database = new DatabaseConfig
    {
        ConnectionString = "Server=localhost;Database=Test;Uid=root;Pwd=123456;"
    },
    Service = new ServiceConfig
    {
        BaseUrl = "https://api.demo.com"
    }
};
YamlConfigHelper.WriteConfig(config, "appsettings.yaml");

// 读取配置
var readConfig = YamlConfigHelper.ReadConfig<AppConfig>("appsettings.yaml");
Console.WriteLine($"应用名称:{readConfig.AppName}");
Console.WriteLine($"数据库连接串:{readConfig.Database.ConnectionString}");

总结

YamlDotNet是C#处理YAML数据的最优选择,其易用性、灵活性和兼容性,能完美适配配置文件、数据交换等各类场景。本文从基础到进阶,覆盖环境搭建、核心用法、优化技巧和实战案例,代码可直接复用。掌握本文内容,可轻松解决YAML序列化/反序列化的各类问题,提升开发效率。

相关推荐
大傻^2 小时前
SpringAI2.0 Null Safety 实战:JSpecify 注解体系与 Kotlin 互操作
android·开发语言·人工智能·kotlin·springai
魑魅魍魉都是鬼2 小时前
Java 适配器模式(Adapter Pattern)
java·开发语言·适配器模式
笨笨马甲2 小时前
Qt MQTT
开发语言·qt
Fairy要carry2 小时前
面试-Agent上下文过载、步骤混乱的问题
开发语言·python
程序员Ctrl喵2 小时前
异步编程:Event Loop 与 Isolate 的深层博弈
开发语言·flutter
liuyao_xianhui2 小时前
优选算法_两数之和_位运算_C++
java·开发语言·数据结构·c++·算法·链表·动态规划
IT猿手3 小时前
MATLAB模拟四旋翼无人机飞行,机翼可独立旋转
开发语言·matlab·无人机
代龙涛3 小时前
WordPress 主题开发指南:模板文件、函数与页面选型规则
开发语言·后端·php·wordpress
代码探秘者3 小时前
【大模型应用】6.RAG 场景下的向量+关键词混合检索
java·开发语言·人工智能·python·spring