将 netcore IConfigurationSection 绑定到动态对象 [英] Bind netcore IConfigurationSection to a dynamic object

查看:12
本文介绍了将 netcore IConfigurationSection 绑定到动态对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆