YAML是一种更适合人阅读的文件格式,很多大型的项目像Ruby on Rails都选择YAML作为配置文件的格式。如果项目的配置很少,用JSON或YAML没有多大差别。看看rails项目中的配置文件,如果用JSON写试试什么感受吧。
在《实现自己的.NET Core配置Provider之EF》中已经讲过配置的执行流程,这里不再复述,直接动手。
YamlConfigurationProvider
Yaml是基于文件的,可以直接从FileConfigurationProvider继承,在FileConfigurationProvider实现了监控文件变化并自动重新加载的功能。
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文件的核心,后面会介绍。
YamlConfigurationSource
internal class YamlConfigurationSource : FileConfigurationSource{    public override IConfigurationProvider Build(IConfigurationBuilder builder)    {EnsureDefaults(builder);        return new YamlConfigurationProvider(this);}
}YamlConfigurationSource实现父类的Build方法,返回YamlConfigurationProvider。
AddYamlFile扩展方法
为添加Yaml配置源增加扩展方法。
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);}
}YamlConfigurationFileParser
解析Yaml是核心的功能,目前github有开源的C# Yaml项目:YamlDotNet和SharpYaml 。SharpYaml Fork自YamlDotNet,但做了不少改进并支持Yaml1.2,不过需要netstandard1.6+。YamlDotNet支持Yaml1.1,需要netstandard1.3+。我选择的YamlSharp。
Yaml可表示三种类型的数据:Scalar(标量,如字符串、布尔值、整数等)、Sequence(序列,如数组)和Mapping(映射,如字典,键值对等)。
关于Yaml可以参考阮一峰老师的《YAML 语言教程》。
SharpYaml会把Yaml文件转换为树形结构,然后我们只需要把所有的叶子节点的路径作为字典的键,将叶子节点的值作为字典的值存储起来就可以了。
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"公众号。
相关文章:
- 10分钟就能学会的.NET Core配置 
- 实现自己的.NET Core配置Provider之EF 
原文地址:http://www.cnblogs.com/nianming/p/7097338.html
.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注
