Written in front


The previous 
article discussed the basic content of the file-type configuration. This article discusses the implementation of the JSON-type configuration and understands the implementation of this type of configuration. Other types of configuration implementations can basically be bypassed. 

should be familiar with each other. This figure mainly expresses the implementation of the file configuration, of course, other configurations, including custom configuration, will be implemented in this way.


DotNET_Core_3.0_source-understanding_Configuration_(3)_0.png

 

JSON configuration component related content

This component has four classes

  • JsonConfigurationExtensions
  • JsonConfigurationSource
  • JsonConfigurationFileParser
  • JsonConfigurationProvider

These four classes work together and have clear responsibilities. Together, the JSON type configuration is loaded into memory for use by the corresponding system.

JsonConfigurationFileParser

This class is an inner class that has a private constructor, meaning that the class cannot be instantiated elsewhere and can only be used internally. It has only one public method and is static, as shown below

   1:  public static IDictionary<string, string> Parse(Stream input) => new JsonConfigurationFileParser().ParseStream(input);

This method converts the input data stream into dictionary-type configuration data. The dictionary type is SortedDictionary type and is case-insensitive. Note here. This also echoes the previously mentioned .NET Core Configuration when it is used externally, it is provided to the outside world in a dictionary.

So, how does this class convert data streams to JSON? Let’s continue reading the source code.

   1:  private IDictionary<string, string> ParseStream(Stream input)
   2:   {
   3:      _data.Clear();
   4:   
   5:      using (var reader = new StreamReader(input))
   6:      using (JsonDocument doc = JsonDocument.Parse(reader.ReadToEnd(), new JsonReaderOptions { CommentHandling = JsonCommentHandling.Skip }))
   7:       {
   8:          if (doc.RootElement.Type != JsonValueType.Object)
   9:           {
  10:              throw new FormatException(Resources.FormatError_UnsupportedJSONToken(doc.RootElement.Type));
  11:           }
  12:           VisitElement (doc.RootElement);
  13:       }
  14:   
  15:      return _data;
  16:   }

Through the source code, we know that JsonDocument is used here to process StreamReader data. What is JsonDocument? Through the namespace we know that it is located in System.Text.Json, which is the native .NET Core component of .NET Core. Friends can go to MSDN or GitHub to find relevant information. Do not explain too much here.

The VisitElement method mainly traverses the collection of objects in the JsonElement.EnumerateObject() method. Here, the Stack<string> instance is used to control the data security. Among them, VisitValue is a fairly comprehensive method for handling json, which is comprehensive because it takes into account almost all types of JSON values:

  • JsonValueType.Object
  • JsonValueType.Array
  • JsonValueType.Number
  • JsonValueType.String
  • JsonValueType.True
  • JsonValueType.False
  • JsonValueType.Null

Of course, this method does not deal with each type very silly, mainly for recursive traversal of Object and Array types, in order to jump out of recursion in simple types such as Number, String, etc., and store them in the dictionary, you need Again, the values ​​stored in the dictionary are stored as String types.

At this point, JsonConfigurationFileParser does the job of reading the content from the file and converting it to a key-value pair.

JsonConfigurationSource

This class is relatively simple, because it inherits from FileConfigurationSource. As mentioned earlier, the FileConfigurationSource class has been initially implemented, providing only one Build method to the subclass to rewrite. Its return value is the JsonConfigurationProvider instance.

   1:  /// <summary>
   2:  /// Represents a JSON file as an <see cref="IConfigurationSource"/>.
   3:  /// </summary>
   4:  public class JsonConfigurationSource : FileConfigurationSource
   5:   {
   6:      /// <summary>
   7:      /// Builds the <see cref="JsonConfigurationProvider"/> for this source.
   8:      /// </summary>
   9:      /// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
  10:      /// <returns>A <see cref="JsonConfigurationProvider"/></returns>
  11:       public  override IConfigurationProvider Build (IConfigurationBuilder builder)
  12:       {
  13:          EnsureDefaults(builder);
  14:          return new JsonConfigurationProvider(this);
  15:       }
  16:   }

The EnsureDefaults() method here is mainly to set the FileProvider instance and the exception handling method for the specified load type.

JsonConfigurationProvider

This class is also very simple, it inherits from FileConfigurationProvider, and FileConfigurationProvider itself has been abstracted by common functions. Let’s take a look at the source code of this class.

   1:  /// <summary>
   2:  /// A JSON file based <see cref="FileConfigurationProvider"/>.
   3:  /// </summary>
   4:  public class JsonConfigurationProvider : FileConfigurationProvider
   5:   {
   6:      /// <summary>
   7:      /// Initializes a new instance with the specified source.
   8:      /// </summary>
   9:      /// <param name="source">The source settings.</param>
  10:      public JsonConfigurationProvider(JsonConfigurationSource source) : base(source) { }
  11:   
  12:      /// <summary>
  13:      /// Loads the JSON data from a stream.
  14:      /// </summary>
  15:      /// <param name="stream">The stream to read.</param>
  16:      public override void Load(Stream stream)
  17:       {
  18:          try {
  19:              Data = JsonConfigurationFileParser.Parse(stream);
  20:          } catch (JsonReaderException e)
  21:           {
  22:              throw new FormatException(Resources.Error_JSONParseError, e);
  23:           }
  24:       }
  25:   }

The input parameter type of its constructor is JsonConfigurationSource, which echoes the return new JsonConfigurationProvider( this ) code snippet in the JsonConfigurationSource.Build() method .

The method overrided by JsonConfigurationProvider calls the JsonConfigurationFileParser.Parse(stream) method, so this class is very lightweight.

JsonConfigurationExtensions

This method, everyone is more familiar with, the AddJsonFile () method we usually use is extended in this extension class, the return value is IConfigurationBuilder type, the core method source code is as follows

   1:  /// <summary>
   2:  /// Adds a JSON configuration source to <paramref name="builder"/>.
   3:  /// </summary>
   4:  /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
   5:  /// <param name="provider">The <see cref="IFileProvider"/> to use to access the file.</param>
   6:  /// <param name="path">Path relative to the base path stored in 
   7:  /// <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
   8:  /// <param name="optional">Whether the file is optional.</param>
   9:  /// <param name="reloadOnChange">Whether the configuration should be reloaded if the file changes.</param>
  10:  /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
  11:  public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange)
  12:   {
  13:      if (builder == null)
  14:       {
  15:          throw new ArgumentNullException(nameof(builder));
  16:       }
  17:      if (string.IsNullOrEmpty(path))
  18:       {
  19:          throw new ArgumentException(Resources.Error_InvalidFilePath, nameof(path));
  20:       }
  twenty one:   
  22:      return builder.AddJsonFile(s =>
  23:       {
  24:          s.FileProvider = provider;
  25:          s.Path = path;
  26:          s.Optional = optional;
  27:          s.ReloadOnChange = reloadOnChange;
  28:          s.ResolveFileProvider();
  29:       });
  30:   }

However, you should not look at the number of lines of code in this method. You think that other methods are overloaded with this method. In fact, the method is overloaded.

   1:  /// <summary>
   2:  /// Adds a JSON configuration source to <paramref name="builder"/>.
   3:  /// </summary>
   4:  /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
   5:  /// <param name="configureSource">Configures the source.</param>
   6:  /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
   7:   public  static IConfigurationBuilder AddJsonFile ( this IConfigurationBuilder builder, Action <JsonConfigurationSource> configureSource)
   8:      => builder.Add(configureSource);

This method is finally called by the IConfigurationBuilder.Add() method.

to sum up

By introducing the four classes of the above JSON Configuration component, we know that the component handles the JSON format file, but because of the file-type configuration, its abstract implementation has been implemented in the file-type configuration extension.

From here, we can learn, if one day we need to extend remote configuration, such as Consul, ZK, etc., we can also consider and adopt the design of this architecture. In addition, in the JSON Configuration component, .NET Core aggregates the processing of proprietary functional methods, and the way to focus on the focus is also worth learning.

Finally, JsonConfigurationFileParser gives us an implementation of Stream to JSON. We can use this class as a tool class.

Orignal link:https://www.cnblogs.com/edison0621/p/10891365.html