According to the Config.NET documentation, a JSON array ([]
) can be parsed into an IEnumerable:
Many providers do not support nested structures. Suppose you have the following configuration declaration:
//collection element public interface IArrayElement { string Username { get; } string Password { get; } } //top level configuration public interface IConfig { IEnumerable<IArrayElement> Creds { get; } }
and you would like to to pass two elements which in json represent as follows:
"Creds": [ { "Username": "user1", "Password": "pass1" }, { "Username": "user2", "Password": "pass2" } ]
But what about a JSON object ({}
)? Can an object similarly be parsed into a dictionary structure? In my case, I'm trying to parse a set of colors. Could the "Colors" object below be parsed into a System.Collections.Generic.IDictionary<string, IEnumerable<int>>
, where the key is the name of the color (string
)?
// JSON
"ApplicationConfiguration": {
"Palette": {
"DefaultLineThickness": 4,
"DefaultTextSize": 30,
"Colors": {
"Red": [ 255, 0, 0 ],
"Green": [ 50, 205, 50 ],
"Blue": [ 65, 105, 225 ]
},
...
}
I suppose I could specify an array of color names (IEnumerable<string>
) and a corresponding array of RGB values (IEnumerable<IEnumerable<int>>
) (if that's even possible). Alternatively, I could create a ColourConfiguration class which contained Name and Value properties, the Name being a string
and the Value being an IEnumerable<int>
. It would be much easier for both the developer and the user if a JSON object could be directly parsed into a dictionary, however. This is the motivation for this question.
I have tried the following interface on the JSON above to no avail. A System.NotSupportedException
is thrown when trying to access the Colors
property.
public interface IApplicationConfiguration
{
public interface IPaletteConfiguration
{
[Option(DefaultValue = 4)]
public int DefaultLineThickness { get; set; }
[Option(DefaultValue = 30)]
public int DefaultTextSize { get; set; }
public IDictionary<string, IEnumerable<int>> Colors { get; set; }
}
public IPaletteConfiguration Palette { get; set; }
}
While this does work in standard serialization, it is not supported in Config.Net
Collections
Config.Net supports collections for primitive types and interfaces.
Collection must be always declared as IEnumerable and only have a getter.
At the moment collections are read-only and writing to collections may be supported at some point in future releases.
So we can't use IDictionary<>
directly, we are restricted to using it's IEnumerable definition: IEnumerable<KeyValuePair<TKey, TValue>>
but KeyValuePair<TKey, TValue>
is itself not supported, even when you change the JSON to be an array of string type key and value pairs:
"ColorsThatDontWork": [
{ "Red": "255, 0, 0 " },
{ "Green": " 50, 205, 50 " } ,
{ "Blue": " 65, 105, 225 " }
]
'settings.ApplicationConfiguration.Palette' threw an exception of type 'System.NotSupportedException'
Data: {System.Collections.ListDictionaryInternal}
HResult: -2146233067
HelpLink: null
InnerException: null
Message: "type System.Collections.Generic.KeyValuePair`2[System.String,System.String] on object 'ColorsThatDontWork' is not supported."
Source: "Config.Net"
StackTrace: " at Config.Net.Core.Box.BoxFactory.ValidateSupportedType(ResultBox rb, ValueHandler valueHandler)\r\n at Config.Net.Core.Box.BoxFactory.DiscoverProperties(Type t, ValueHandler valueHandler, Dictionary`2 result, String basePath)\r\n at Config.Net.Core.Box.BoxFactory.Discover(Type t, ValueHandler valueHandler, String basePath)\r\n at Config.Net.Core.InterfaceInterceptor..ctor(Type interfaceType, IoHandler ioHandler, String prefix)\r\n at Config.Net.Core.Box.ProxyResultBox.InitialiseAt(Int32 index, IoHandler ioHandler, String prefix)\r\n at Config.Net.Core.DynamicReader.ReadProxy(ProxyResultBox xbox, Int32 index)\r\n at Config.Net.Core.DynamicReader.Read(ResultBox rbox, Int32 index, Object[] arguments)\r\n at Config.Net.Core.InterfaceInterceptor.Intercept(IInvocation invocation)\r\n at Castle.DynamicProxy.AbstractInvocation.Proceed()\r\n at Castle.Proxies.IApplicationConfigurationProxy.get_Palette()"
TargetSite: {Void ValidateSupportedType(Config.Net.Core.Box.ResultBox, Config.Net.Core.ValueHandler)}
Before we worry about nested IEnumerable<int>
as an expected type parameter to the value of the dictionary, Config.Net doesn't even support reading JSON object structures of string properties into a dictionary.
If you need these structures in your configuration then it might be time to start using the standard options binding in .Net Core: Options pattern in .NET
Config.Net is no longer an active project, however you could try posting an issue on the the project repo: https://github.com/aloneguid/config
It solved a problem back in .Net Framework but .Net Core specifically provided a new solution for managing app configuration that does cater for your scenario so it would be worth investigating how much effort it might take to adopt it, or if you are stuck in .net framework you could still use JSON.Net to deserialize your JSON settings.