将netcore IConfigurationSection绑定到动态对象 [英] Bind netcore IConfigurationSection to a dynamic object
问题描述
在我的appSettings.json中,我有一个配置节,可以包含任何东西,只要它是json有效的即可.通常是一组键/值(字符串/字符串)
In my appSettings.json, I have a configuration section that can contain anything as long as it's json valid. It is usally a set of key/value (string/string)
我想在我的代码中获取它,并在控制器调用中将其返回.
I would like to get it in my code and return it in a controller call.
我看了一下源代码( https://github.com/aspnet/Configuration/blob/6d9519622b5db2c5ac6bafa8bcdb25fe27914de3/src/Config.Binder/ConfigurationBinder.cs ),看来我注定要采用现成的解决方案.
I took a look at the source code (https://github.com/aspnet/Configuration/blob/6d9519622b5db2c5ac6bafa8bcdb25fe27914de3/src/Config.Binder/ConfigurationBinder.cs ) and it seems I am doomed with off-the-shelves solutions.
如果将用例限制为键值对,则可以在IConfigSection中使用AsEnumerable(),这很好.如果我想允许列表,那么我仍然可以解析键以查找:Number,但是有人可以轻松地反序列化随机对象吗?甚至更好地按原样获取配置部分,而无需反序列化它.
If I limit the use case to key value pairs, I can use the AsEnumerable() in the IConfigSection and that's fine. If I want to allow lists, then I may still be ok parsing the keys to look for :Number but does someone has a way to easily deserialize a random object ? Or even better get the configuration section as is without deserializing it.
例如
{
"mySettings":
{
"key1": "value1",
"key2": "value2",
"list": [ "item1", "item2", "item3" ],
"complexObject": {
"key": "value",
"anything" : [{"id": "3", "name": "John"}]
}
}
}
推荐答案
如果您滥用.NET 4动态对象,则有可能.如您所说,您可以枚举配置中的所有键,并且它们都遵循相同的模式.在您的示例中,所有感兴趣的关键是:
It's possible if you abuse .NET 4 dynamic objects. As you said, you can enumerate over all keys in the config, and they all follow the same pattern. With your example, all the keys of interest are:
mySettings null
mySettings:list null
mySettings:list:2 item3
mySettings:list:1 item2
mySettings:list:0 item1
mySettings:key3 value3
mySettings:key2 value2
mySettings:key1 value1
mySettings:complexObject null
mySettings:complexObject:key value
mySettings:complexObject:anything null
mySettings:complexObject:anything:0 null
mySettings:complexObject:anything:0:name John
mySettings:complexObject:anything:0:id 3
由此,我们可以构建一个ExpandoObject
,如下所示:
From this, we can build an ExpandoObject
, like so:
[HttpGet]
public IActionResult Get([FromServices] IConfiguration config)
{
var result = new ExpandoObject();
// retrieve all keys from your settings
var configs = config.AsEnumerable().Where(_ => _.Key.StartsWith("mySettings"));
foreach(var kvp in configs)
{
var parent = result as IDictionary<string, object>;
var path = kvp.Key.Split(':');
// create or retrieve the hierarchy (keep last path item for later)
var i = 0;
for (i = 0; i < path.Length - 1; i++)
{
if (!parent.ContainsKey(path[i]))
{
parent.Add(path[i], new ExpandoObject());
}
parent = parent[path[i]] as IDictionary<string, object>;
}
if (kvp.Value == null)
continue;
// add the value to the parent
// note: in case of an array, key will be an integer and will be dealt with later
var key = path[i];
parent.Add(key, kvp.Value);
}
// at this stage, all arrays are seen as dictionaries with integer keys
ReplaceWithArray(null, null, result);
return Ok(result);
}
private void ReplaceWithArray(ExpandoObject parent, string key, ExpandoObject input)
{
if (input == null)
return;
var dict = input as IDictionary<string, object>;
var keys = dict.Keys.ToArray();
// it's an array if all keys are integers
if (keys.All(k => int.TryParse(k, out var dummy))) {
var array = new object[keys.Length];
foreach(var kvp in dict) {
array[int.Parse(kvp.Key)] = kvp.Value;
// Edit: If structure is nested deeper we need this next line
ReplaceWithArray(input, kvp.Key, kvp.Value as ExpandoObject);
}
var parentDict = parent as IDictionary<string, object>;
parentDict.Remove(key);
parentDict.Add(key, array);
}
else
{
foreach (var childKey in dict.Keys.ToList())
{
ReplaceWithArray(input, childKey, dict[childKey] as ExpandoObject);
}
}
}
注意:由于冒号:
用作分隔符,因此您不能拥有包含冒号的键.
Note: since the colon :
is used as separator, you can't have a key that contains a colon.
最后,由于您现在有了一个动态对象,因此可以直接获取其属性:
Finally, because you now have a dynamic object, you can directly get its properties:
dynamic asDym = result;
string name = asDym.mySettings.complexObject.anything[0].name;
这篇关于将netcore IConfigurationSection绑定到动态对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!