gimmi
9/10/2019 - 11:48 AM

JObjectConfigurationSource.cs

// Use like this:
// var hostBuilder = new HostBuilder()
//     .ConfigureHostConfiguration(cfg => {
//         cfg.Add(new JObjectConfigurationSource(new JObject()));
//     })

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;

namespace MyNamespace
{
    public class JObjectConfigurationSource : ConfigurationProvider, IConfigurationSource
    {
        private readonly JObject _jObject;
        private readonly IDictionary<string, string> _data = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        private readonly Stack<string> _context = new Stack<string>();
        private string _currentPath;

        public JObjectConfigurationSource(JObject jObject) => _jObject = jObject;

        public IConfigurationProvider Build(IConfigurationBuilder builder) => this;

        public override void Load()
        {
            Data = ParseStream(_jObject);
        }

        private IDictionary<string, string> ParseStream(JObject jObject)
        {
            _data.Clear();
            VisitJObject(jObject);
            return _data;
        }

        public void VisitJObject(JObject jObject)
        {
            foreach (var property in jObject.Properties())
            {
                EnterContext(property.Name);
                VisitProperty(property);
                ExitContext();
            }
        }

        private void VisitProperty(JProperty property)
        {
            VisitToken(property.Value);
        }

        private void VisitToken(JToken token)
        {
            switch (token.Type)
            {
                case JTokenType.Object:
                    VisitJObject(token.Value<JObject>());
                    break;
                case JTokenType.Array:
                    VisitArray(token.Value<JArray>());
                    break;
                case JTokenType.Integer:
                case JTokenType.Float:
                case JTokenType.String:
                case JTokenType.Boolean:
                case JTokenType.Null:
                case JTokenType.Raw:
                case JTokenType.Bytes:
                    VisitPrimitive(token.Value<JValue>());
                    break;
                default:
                    throw new FormatException();
            }
        }

        private void VisitArray(JArray array)
        {
            for (int index = 0; index < array.Count; ++index)
            {
                EnterContext(index.ToString());
                VisitToken(array[index]);
                ExitContext();
            }
        }

        private void VisitPrimitive(JValue data)
        {
            string currentPath = _currentPath;
            if (_data.ContainsKey(currentPath))
            {
                throw new FormatException();
            }

            _data[currentPath] = data.ToString(CultureInfo.InvariantCulture);
        }

        private void EnterContext(string context)
        {
            _context.Push(context);
            _currentPath = ConfigurationPath.Combine(_context.Reverse());
        }

        private void ExitContext()
        {
            _context.Pop();
            _currentPath = ConfigurationPath.Combine(_context.Reverse());
        }
    }
}