【问题标题】:Loading startup configuration from JSON located in memory从位于内存中的 JSON 加载启动配置
【发布时间】:2017-06-28 16:19:19
【问题描述】:

我正在研究如何在 .NET Core 中加载启动配置。我注意到有不同的方法可以做到这一点,我已经看到了 XML、JSON、init 文件,以及来自位于内存中的 Dictionary(我稍后会回到这个)。我正在使用类似下面的代码:

new ConfigurationBuilder().AddJsonFile("file.json").Build();

所有这些都可以,但是,是否有任何方法可以从 JSON 字符串加载该配置?我的意思是,我不想将 json 存储在临时文件中,因为它是一个实时构建的文件,而且没有任何意义。

关于位于内存中的字典。手动构建它很容易,但是,对于复杂且过于分层的 JSON 结构呢?据我所知,字典是

字典

其键是由“:”连接的树的父级,同时管理重复节点、枚举它们等。从头开始构建这个算法很痛苦。

【问题讨论】:

  • 您也可以从字符串中读取 JSON。检查此链接stackoverflow.com/a/29843542/2298362
  • @PrabodhM 感谢您的评论,但这与我的问题无关。我要问的是:“有没有办法从内存中的 json 加载 ConfigurationBuilder()?”
  • 对不起,我想我误解了你的问题。

标签: json .net-core build configuration


【解决方案1】:

使用 NuGet 包Microsoft.Extensions.Configuration.Json >= 3.0.0

var json = "{ \"option1\": 1, \"option2\": \"abc\", }";
var configuration = new ConfigurationBuilder().AddJsonStream(new MemoryStream(Encoding.ASCII.GetBytes(json))).Build();

【讨论】:

  • 当最初的问题被问到时,这个解决方案可能不可用,但肯定是直接从 JSON 字符串构建配置的最简单方法。非常适合单元测试!
  • 当心运行多个 .Build() 时使用 AddJsonStream 会出现问题。刚刚遇到这个问题,因为 IConfigurationBuilder 上的第一个 .Build() 将破坏初始流。错误报告在这里:github.com/dotnet/extensions/issues/3645
【解决方案2】:

我非常喜欢 Adam 的回答,但他链接的接口实现有点单一。这是一个较小的:

public class InMemoryFileProvider : IFileProvider
{
    private class InMemoryFile : IFileInfo
    {
        private readonly byte[] _data;
        public InMemoryFile(string json) => _data = Encoding.UTF8.GetBytes(json);
        public Stream CreateReadStream() => new MemoryStream(_data);
        public bool Exists { get; } = true;
        public long Length => _data.Length;
        public string PhysicalPath { get; } = string.Empty;
        public string Name { get; } = string.Empty;
        public DateTimeOffset LastModified { get; } = DateTimeOffset.UtcNow;
        public bool IsDirectory { get; } = false;
    }

    private readonly IFileInfo _fileInfo;
    public InMemoryFileProvider(string json) => _fileInfo = new InMemoryFile(json);
    public IFileInfo GetFileInfo(string _) => _fileInfo;
    public IDirectoryContents GetDirectoryContents(string _) => null;
    public IChangeToken Watch(string _) => NullChangeToken.Singleton;
}

然后,根据亚当的回答,您可以使用:

var memoryFileProvider = new InMemoryFileProvider(jsonString);
var configuration = new ConfigurationBuilder()
    .AddJsonFile(memoryFileProvider, "appsettings.json", false, false)
    .Build();

【讨论】:

    【解决方案3】:

    在 ASPNETCORE 2.0 中(不确定其他版本)你可以使用config.AddInMemoryCollection:

    var host = new WebHostBuilder()
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.AddInMemoryCollection(new Dictionary<string, string>()
            {
                { "MyParentKey:MySubKey", "MyValue" }
            });
        });
    

    更新: 我已经修改了下面链接中的一些代码来解析 JSON 字符串并返回字典:

    https://github.com/aspnet/Configuration/blob/d469707ab18eef7ed0002f00175a9ad5b0f36250/src/Config.Json/JsonConfigurationFileParser.cs

    using Microsoft.Extensions.Configuration;
    using Newtonsoft.Json.Linq;
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace Config
    {
        public class JsonConfigurationParser
        {
            private JsonConfigurationParser() { }
    
            private readonly IDictionary<string, string> _data = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            private readonly Stack<string> _context = new Stack<string>();
            private string _currentPath;
    
            public static IDictionary<string, string> Parse(string json) => new JsonConfigurationParser().ParseJson(json);
    
            private IDictionary<string, string> ParseJson(string json)
            {
                _data.Clear();
    
                var jsonConfig = JObject.Parse(json);
    
                VisitJObject(jsonConfig);
    
                return _data;
            }
    
            private 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.Bytes:
                    case JTokenType.Raw:
                    case JTokenType.Null:
                        VisitPrimitive(token.Value<JValue>());
                        break;
    
                    default:
                        throw new FormatException("Unsupported JSON token");
                }
            }
    
            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)
            {
                var key = _currentPath;
    
                if (_data.ContainsKey(key))
                {
                    throw new FormatException("Duplicate Key");
                }
                _data[key] = 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());
            }
        }
    }
    

    用法:

    var dictionary = JsonConfigurationParser.Parse(MyJsonString);
    

    【讨论】:

    • 答案有帮助,但仍不完整。您将如何对复杂的 json 对象进行建模,这作为字典会很乏味。我们正在寻找一种直接加载 JSON 字符串的方法。
    • @JohnKoz - Github 中有一些 MS 代码已经将 JSON 转换为正确的字典格式(它是一个内部类)。我已经对其进行了调整并将其添加到答案中。希望这会有所帮助。
    • 谢谢,您的初次回复非常好。非常适合在 dotnetcore 2 上进行单元测试
    【解决方案4】:

    您可以通过实现内存中文件提供程序相对轻松地重用现有 API (Microsoft.Extensions.Configuration.Json)。

    你需要

    • 一个虚拟的 IFileProvider 实现 - 类似于使用的 here
    • 一个虚拟的 IFileInfo 实现 - 你可以借用一个 from here

    下面的代码演示了如何组装所有这些:

    var json = "{ \"option1\": 1, \"option2\": \"abc\", }";
    var memoryJsonFile = new MemoryFileInfo("config.json", Encoding.UTF8.GetBytes(json), DateTimeOffset.Now);
    var memoryFileProvider = new MockFileProvider(memoryJsonFile);
    
    var configuration = new ConfigurationBuilder()
        .AddJsonFile(memoryFileProvider, "config.json", false, false)
        .Build();
    
    Console.WriteLine(configuration["option2"]);
    

    你去吧;)

    【讨论】:

      【解决方案5】:

      您可以使用.AddJsonStream

      var jsonString =  "{...}"    
      var configuration = new ConfigurationBuilder()
                                 .AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
                                 .Build();
      

      你需要

      <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
      

      【讨论】:

        猜你喜欢
        • 2011-06-24
        • 1970-01-01
        • 1970-01-01
        • 2023-04-04
        • 1970-01-01
        • 2017-11-11
        • 1970-01-01
        • 2015-05-11
        • 2012-08-03
        相关资源
        最近更新 更多