YAML是一種更適合人閱讀的文件格式,不少大型的項目像Ruby on Rails都選擇YAML做爲配置文件的格式。若是項目的配置不多,用JSON或YAML沒有多大差異。看看rails項目中的配置文件,若是用JSON寫試試什麼感覺吧。html
在《實現本身的.NET Core配置Provider之EF》中已經講過配置的執行流程,這裏再也不復述,直接動手。node
Yaml是基於文件的,能夠直接從FileConfigurationProvider
繼承,在FileConfigurationProvider實現了監控文件變化並自動從新加載的功能。git
internal class YamlConfigurationProvider : FileConfigurationProvider { public YamlConfigurationProvider(FileConfigurationSource source) : base(source) { } public override void Load(Stream stream) { var parser = new YamlConfigurationFileParser(); Data = parser.Parse(stream); } }
YamlConfigurationParser
是解析Yaml文件的核心,後面會介紹。github
internal class YamlConfigurationSource : FileConfigurationSource { public override IConfigurationProvider Build(IConfigurationBuilder builder) { EnsureDefaults(builder); return new YamlConfigurationProvider(this); } }
YamlConfigurationSource
實現父類的Build方法,返回YamlConfigurationProvider
。數組
爲添加Yaml配置源增長擴展方法。bash
public static class YamlConfigurationExtensions { public static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, string path) { return AddYamlFile(builder, provider: null, path: path, optional: false, reloadOnChange: false); } public static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, string path, bool optional) { return AddYamlFile(builder, provider: null, path: path, optional: optional, reloadOnChange: false); } public static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) { return AddYamlFile(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange); } public static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (string.IsNullOrEmpty(path)) { throw new ArgumentException(Resources.Error_InvalidFilePath, nameof(path)); } return builder.AddYamlFile(s => { s.FileProvider = provider; s.Path = path; s.Optional = optional; s.ReloadOnChange = reloadOnChange; s.ResolveFileProvider(); }); } internal static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, Action<YamlConfigurationSource> configureSource) { var source = new YamlConfigurationSource(); configureSource(source); return builder.Add(source); } }
解析Yaml是核心的功能,目前github有開源的C# Yaml項目:YamlDotNet和SharpYaml 。SharpYaml Fork自YamlDotNet,但作了很多改進並支持Yaml1.2,不過須要netstandard1.6+。YamlDotNet支持Yaml1.1,須要netstandard1.3+。我選擇的YamlSharp。app
Yaml可表示三種類型的數據:Scalar(標量,如字符串、布爾值、整數等)、Sequence(序列,如數組)和Mapping(映射,如字典,鍵值對等)。ide
關於Yaml能夠參考阮一峯老師的《YAML 語言教程》。ui
SharpYaml會把Yaml文件轉換爲樹形結構,而後咱們只須要把全部的葉子節點的路徑做爲字典的鍵,將葉子節點的值做爲字典的值存儲起來就能夠了。this
internal class YamlConfigurationFileParser { private readonly IDictionary<string, string> _data = new SortedDictionary<string, string>(StringComparer.Ordinal); private readonly Stack<string> _context = new Stack<string>(); private string _currentPath; public IDictionary<string, string> Parse(Stream input) { _data.Clear(); _context.Clear(); var yaml = new YamlStream(); yaml.Load(new StreamReader(input)); if (yaml.Documents.Count > 0) { var rootNode = yaml.Documents[0].RootNode; VisitYamlNode("", rootNode); } return _data; } private void VisitYamlNode(string context, YamlNode node) { if (node is YamlScalarNode) { VisitYamlScalarNode(context, (YamlScalarNode)node); } else if (node is YamlMappingNode) { VisitYamlMappingNode(context, (YamlMappingNode)node); } else if (node is YamlSequenceNode) { VisitYamlSequenceNode(context, (YamlSequenceNode)node); } } private void VisitYamlScalarNode(string context, YamlScalarNode node) { EnterContext(context); if (_data.ContainsKey(_currentPath)) { throw new FormatException(string.Format(Resources.Error_KeyIsDuplicated, _currentPath)); } _data[_currentPath] = node.Value; ExitContext(); } private void VisitYamlMappingNode(string context, YamlMappingNode node) { EnterContext(context); foreach (var yamlNode in node.Children) { context = ((YamlScalarNode)yamlNode.Key).Value; VisitYamlNode(context, yamlNode.Value); } ExitContext(); } private void VisitYamlSequenceNode(string context, YamlSequenceNode node) { EnterContext(context); for (int i = 0; i < node.Children.Count; i++) { VisitYamlNode(i.ToString(), node.Children[i]); } ExitContext(); } private void EnterContext(string context) { if (!string.IsNullOrEmpty(context)) { _context.Push(context); } _currentPath = ConfigurationPath.Combine(_context.Reverse()); } private void ExitContext() { if (_context.Any()) { _context.Pop(); } _currentPath = ConfigurationPath.Combine(_context.Reverse()); } }
本項目已在github上開源,地址:https://github.com/chengxulvtu/Cxlt.Extensions.Configuration
在項目中使用能夠執行下面的命令
Install-Package Cxlt.Extensions.Configuration.Yaml
或
dotnet add package Cxlt.Extensions.Configuration.Yaml
若是這篇文章對你有幫助或有什麼問題,歡迎關注「chengxulvtu"公衆號。