using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Resources; using YamlDotNet.RepresentationModel; //Yaml流 扩展功能 namespace OptionStudy.UnitApp { /// /// Yaml流 配置源 /// public class YamlStreamConfigurationSource : StreamConfigurationSource { /// /// Builds the for this source. /// /// The . /// An public override IConfigurationProvider Build(IConfigurationBuilder builder) { return new YamlStreamConfigurationProvider(this); } } /// /// Yaml流配置 提供者 /// public class YamlStreamConfigurationProvider : StreamConfigurationProvider { /// /// Constructor. /// /// The . /// public YamlStreamConfigurationProvider(YamlStreamConfigurationSource source) : base(source) { } /// /// Loads json configuration key/values from a stream into a provider. /// /// The json to load configuration data from. public override void Load(Stream stream) { var parser = new YamlFileParser(); Data = parser?.Parse(stream)??new Dictionary(); } } /// /// Yaml流 扩展方法类 /// public static class YamlConfigurationExtensions { public static IConfigurationBuilder AddYamlStreamFile(this IConfigurationBuilder builder, Stream stream) { Stream stream2 = stream; return builder.Add(delegate (YamlStreamConfigurationSource s) { s.Stream = stream2; }); } } /// /// 解析类:复制原库,因为原库类为 internal /// public class YamlFileParser { private readonly IDictionary _data = new SortedDictionary(StringComparer.OrdinalIgnoreCase); private readonly Stack _context = new Stack(); private string? _currentPath; public IDictionary Parse(Stream input) { _data.Clear(); _context.Clear(); // https://dotnetfiddle.net/rrR2Bb var yaml = new YamlStream(); yaml.Load(new StreamReader(input, detectEncodingFromByteOrderMarks: true)); if (yaml.Documents.Any()) { var mapping = (YamlMappingNode)yaml.Documents[0].RootNode; // The document node is a mapping node VisitYamlMappingNode(mapping); } return _data; } private void VisitYamlNodePair(KeyValuePair yamlNodePair) { var context = ((YamlScalarNode)yamlNodePair.Key).Value??string.Empty; VisitYamlNode(context, yamlNodePair.Value); } private void VisitYamlNode(string context, YamlNode node) { if (node is YamlScalarNode scalarNode) { VisitYamlScalarNode(context, scalarNode); } if (node is YamlMappingNode mappingNode) { VisitYamlMappingNode(context, mappingNode); } if (node is YamlSequenceNode sequenceNode) { VisitYamlSequenceNode(context, sequenceNode); } } private void VisitYamlScalarNode(string context, YamlScalarNode yamlValue) { //a node with a single 1-1 mapping EnterContext(context); var currentKey = _currentPath??string.Empty; if (_data.ContainsKey(currentKey)) { throw new FormatException(Resources.FormatError_KeyIsDuplicated(currentKey)); } _data[currentKey] = IsNullValue(yamlValue) ? null : yamlValue.Value; ExitContext(); } private void VisitYamlMappingNode(YamlMappingNode node) { foreach (var yamlNodePair in node.Children) { VisitYamlNodePair(yamlNodePair); } } private void VisitYamlMappingNode(string context, YamlMappingNode yamlValue) { //a node with an associated sub-document EnterContext(context); VisitYamlMappingNode(yamlValue); ExitContext(); } private void VisitYamlSequenceNode(string context, YamlSequenceNode yamlValue) { //a node with an associated list EnterContext(context); VisitYamlSequenceNode(yamlValue); ExitContext(); } private void VisitYamlSequenceNode(YamlSequenceNode node) { for (int i = 0; i < node.Children.Count; i++) { VisitYamlNode(i.ToString(), node.Children[i]); } } private void EnterContext(string context) { _context.Push(context); _currentPath = ConfigurationPath.Combine(_context.Reverse()); } private void ExitContext() { _context.Pop(); _currentPath = ConfigurationPath.Combine(_context.Reverse()); } private bool IsNullValue(YamlScalarNode yamlValue) { return yamlValue.Style == YamlDotNet.Core.ScalarStyle.Plain && ( yamlValue.Value == "~" || yamlValue.Value == "null" || yamlValue.Value == "Null" || yamlValue.Value == "NULL" ); } } /// /// 解析依赖类:复制原库,因为原库类为 internal /// public static class Resources { private static readonly ResourceManager _resourceManager = new ResourceManager("NetEscapades.Configuration.Yaml.Resources", typeof(Resources).GetTypeInfo().Assembly); /// /// The configuration file '{0}' was not found and is not optional. /// internal static string Error_FileNotFound { get { return GetString("Error_FileNotFound"); } } /// /// The configuration file '{0}' was not found and is not optional. /// internal static string FormatError_FileNotFound(object p0) { return string.Format(CultureInfo.CurrentCulture, GetString("Error_FileNotFound"), p0); } /// /// File path must be a non-empty string. /// internal static string Error_InvalidFilePath { get { return GetString("Error_InvalidFilePath"); } } /// /// File path must be a non-empty string. /// internal static string FormatError_InvalidFilePath() { return GetString("Error_InvalidFilePath"); } /// /// Could not parse the Yaml file. Error on line number '{0}': '{1}'. /// internal static string Error_YamlParseError { get { return GetString("Error_YamlParseError"); } } /// /// Could not parse the YAML file: {0}. /// internal static string FormatError_YamlParseError(object p0) { return string.Format(CultureInfo.CurrentCulture, GetString("Error_YamlParseError"), p0); } /// /// A duplicate key '{0}' was found. /// internal static string Error_KeyIsDuplicated { get { return GetString("Error_KeyIsDuplicated"); } } /// /// A duplicate key '{0}' was found. /// internal static string FormatError_KeyIsDuplicated(object p0) { return string.Format(CultureInfo.CurrentCulture, GetString("Error_KeyIsDuplicated"), p0); } private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); System.Diagnostics.Debug.Assert(value != null); if (formatterNames != null) { for (var i = 0; i < formatterNames.Length; i++) { value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); } } return value; } } }