38

I am researching about how to load the startup config in .NET Core. I notice there are different ways to do that, I have seen XML, JSON, init file, and from a Dictionary located in memory (I will go back to this later). I'm using something like the code below:

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

All of that is OK, but, isn't there any way to load that configuration from a JSON string? I mean, I don't want to store the json in a temporal file because is a real-time built file and It makes no sense.

About the dictionary located in memory. Its easy to build it manually, but, What about for complex and too hierarchical JSON structures? As far as I know, the dictionary is

dictionary < string, string >

whose key is the parents of the tree concatenated by ":" managing at the same time the repeated nodes, enumerating them, etc. A pain build this algorithm from scratch.

3
  • You can read JSON from string as well. Check this link stackoverflow.com/a/29843542/2298362 Commented Jun 28, 2017 at 16:33
  • @PrabodhM Thanks for your comment, but It is not related to my question. What I'm questioning is: "Is there any way to load a ConfigurationBuilder() from a json in memory?" Commented Jun 28, 2017 at 17:29
  • Sorry I guess I misinterpreted your question. Commented Jun 28, 2017 at 17:36

6 Answers 6

55

Use NuGet package 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();
Sign up to request clarification or add additional context in comments.

2 Comments

Possibly this solution wasn't available when the original question was asked, but certainly seems like the easiest way to build a configuration directly from a JSON string. Perfect for unit testing!
Beware there are issues using AddJsonStream when multiple .Build()'s are run. Just ran into this issue cause the first .Build() on a IConfigurationBuilder will destroy the initial streams. Bug report here: github.com/dotnet/extensions/issues/3645
17

I liked Adam's answer a lot, but the interface implementations he linked to were a bit monolithic. Here is a smaller one:

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;
}

Then, as per Adam's answer, you can use:

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

Comments

7

You can re-use the existing API (Microsoft.Extensions.Configuration.Json) relatively easily by implementing an in-memory file provider.

You'll need

  • a dummy IFileProvider implementation - something like the one used here.
  • a dummy IFileInfo implementation - you can borrow one from here.

The code below demonstrates how to assemble all this:

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"]);

And there you go ;)

Comments

7

In ASPNETCORE 2.0 (not sure about other versions) you can use config.AddInMemoryCollection:

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

UPDATE: I've adapted some code from the link below to parse a JSON string and return a Dictionary:

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());
        }
    }
}

USAGE:

var dictionary = JsonConfigurationParser.Parse(MyJsonString);

3 Comments

Answer helps, but is still not complete. How would you model a complex json object, this would be tedious as a dictionary. We're looking for some way just to load a JSON string directly.
@JohnKoz - There's some MS code in Github that already converts JSON to the correct dictionary format (it's an internal class). I've adapted it and added it to the answer. Hopefully that helps.
Thank you, your initial reply is very nice. Works very well for unit testing on dotnetcore 2
2

With raw string, it is even easier (answer like accepted, but with raw string).

var json =
    """
    {
        "option1": 1,
        "option2": "abc"
    }
    """;

var configuration1 = new ConfigurationBuilder()
    .AddJsonStream(new MemoryStream(Encoding.ASCII.GetBytes(json)))
    .Build();

Comments

-1

You can use .AddJsonStream

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

you will need

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

1 Comment

This is the same as the above answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.