The following program:

using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;

namespace ConsoleApp3
{
    class Program
    {
        static void Main()
        {
            var builder = new ConfigurationBuilder();
            builder.AddJsonFile("appsettings.json");

            var configuration = builder.Build();
            var options = configuration.Get<Options>();
            foreach (var kvp in options.Values)
                Console.WriteLine($"{kvp.Key}: {kvp.Value}");
        }
    }

    internal class Options
    {
        public Dictionary<string, bool> Values { get; } = new Dictionary<string, bool>();
    }
}

When given this appsettings.json file, runs perfectly:

{
    "Values": {
        "a": true,
        "b": false
    }
}

but change the appsettings.json contents to this:

{
    "Values": {
        "a:b": true,
        "b": false
    }
}

and I get this exception:

Unhandled Exception: System.InvalidOperationException: Cannot create instance of type 'System.Boolean' because it is missing a public parameterless constructor.

stack trace:

   at Microsoft.Extensions.Configuration.ConfigurationBinder.CreateInstance(Type type)
   at Microsoft.Extensions.Configuration.ConfigurationBinder.BindInstance(Type type, Object instance, IConfiguration config, BinderOptions options)
   at Microsoft.Extensions.Configuration.ConfigurationBinder.BindDictionary(Object dictionary, Type dictionaryType, IConfiguration config, BinderOptions options)
   at Microsoft.Extensions.Configuration.ConfigurationBinder.BindInstance(Type type, Object instance, IConfiguration config, BinderOptions options)
   at Microsoft.Extensions.Configuration.ConfigurationBinder.BindProperty(PropertyInfo property, Object instance, IConfiguration config, BinderOptions options)
   at Microsoft.Extensions.Configuration.ConfigurationBinder.BindNonScalar(IConfiguration configuration, Object instance, BinderOptions options)
   at Microsoft.Extensions.Configuration.ConfigurationBinder.BindInstance(Type type, Object instance, IConfiguration config, BinderOptions options)
   at Microsoft.Extensions.Configuration.ConfigurationBinder.Get[T](IConfiguration configuration, Action`1 configureOptions)
   at ConsoleApp3.Program.Main() in D:\Dev\ConsoleApp3\ConsoleApp3\Program.cs:line 15

What am I doing wrong? Note that having a colon in the key is perfectly legal json, but perhaps storing any odd dictionary in the appsettings.json file is not supported?

1

There are 1 best solutions below

0
On BEST ANSWER

Reference Configuration in ASP.NET Core: Hierarchical configuration data

The Configuration API is capable of maintaining hierarchical configuration data by flattening the hierarchical data with the use of a delimiter in the configuration keys.

When the file is read into configuration, unique keys are created to maintain the original hierarchical data structure of the configuration source. The sections and keys are flattened with the use of a colon (:) to maintain the original structure

That means that in the following appsettings.json file

{
    "Values": {
        "a:b": true,
        "b": false
    }
}

The keys would be flattened to

  • Values:a:b
  • Values:b

which is going to break the structure of settings file when the ConfigurationBinder.BindDictionary tries to bind the Dictionary<string, bool> property in the Options

Also referencing this GitHub Issue

Colons are reserved for special meaning in the keys, so they shouldn't be used as part of normal key values.