1

Given the following json configuration file some_settings.json

{
    "AnEmptyArray": [],
    "APopulatedArray": [1, 2, 3]
}

When adding as a source to the asp.net core configuration system and requesting all the keys

var configuration = new ConfigurationBuilder()
    .AddJsonFile("some_settings.json")
    .Build();

var keys = configuration
    .AsEnumerable()
    .Select(c => c.Key);

The results are

APopulatedArray 
APopulatedArray:2 
APopulatedArray:1 
APopulatedArray:0 

For some reason, there is no key for AnEmptyArray

The results are the same if I remove the AnEmptyArray key completly from the json

{
    "APopulatedArray": [1, 2, 3]
}

This means there is no way of detecting the difference between an empty array and the array not being provided at all.

Is this correct?

2
  • Why are you getting 2,1,0 when you passed 1,2,3 ?? Are all data correct in the question? Commented Feb 8, 2018 at 2:07
  • They are the array indices, nothing to do with the values. Thats the way the configuration system names array keys. Commented Feb 8, 2018 at 2:32

1 Answer 1

1

This means there is no way of detecting the difference between an empty array and the array not being provided at all.

Is this correct?

If shortly - yes.

To understand the reason, we should dig into the .Net Core configuration loading mechanism. Adding every new source, like AddJsonFile() or AddEnvironmentVariables(), actually ads another instance of IConfigurationSource to IConfigurationBuilder. When you finally call IConfigurationBuilder.Build() method, you get instance of IConfigurationRoot, which is essentially a holder for a list of loaded configuration providers:

public interface IConfigurationRoot : IConfiguration
{
    IEnumerable<IConfigurationProvider> Providers { get; }
    void Reload();
}

Goring further, IConfigurationProvider operates only with configuration key-values pairs:

public interface IConfigurationProvider
{
    IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath);
    IChangeToken GetReloadToken();
    void Load();
    void Set(string key, string value);
    bool TryGet(string key, out string value);
}

IConfigurationProvider does not care at all about any initial structure of your settings, like arrays or sub-sections:

enter image description here

Such mechanism has several advantages:

  1. You could have different parts of configuration (even for one section) in different sources.
  2. You could override specific key values of a section (keeping other values for a section) in a different source.

So answer to your question is "Yes", you can't detect the difference between an empty array and missing array through standard configuration mechanism. Initial JSON structure is just lost and configuration is stored as key-value pairs. Empty arrays does not produce any key-value pair in IConfigurationProvider.

Sign up to request clarification or add additional context in comments.

4 Comments

But why do empty arrays not produce any key-value pair?. The consequence is that I am unable to bind an empty array. This seems like an oversight.
What key-value do you expect from empty array? Array produces values like Array:0, Array:1, ... Array itself is not a terminal (leaf) configuration value, it's just a holder for further settings. "I am unable to bind an empty array" - if your POCO settings class has an array property, just initialize it with empty array in constructor. Configuration binder will leave it as is. As result you will get empty setting array. Why this would cause the problems? Why do you need to distinguish empty and missing arrays for your configuration?
I am building my own library on top of the configuration system. Given a POCO setting, is will throw an exception if a property doesn't have a configuration value set to prevent you from adding a property but forgetting to set it in configuration. Unfortunately this issue means that for array, I cannot tell whether an array was provided or not as an empty array is treated that same as not added it to configuration at all.
You're talking about JSON. But consider other configuration sources, like environment variables, command line, Azuer Key Vault, etc. Most configuration sources do not have any structure at all, and use key-value pairs at input. What if your configuration is loaded fully from environemnt variables? In this case you will have pairs like variable Array:0 set to Value. And there is even no way to input empty array. So if you want your library to fully support .Net Core Configuration idioms, you should not throw if some setting is missing. Built-in binder works in this way. You should follow.

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.