簡體   English   中英

使用Newtonsoft解析.net中的配置文件 5

[英]use Newtonsoft to parse Configuration files in .net 5

我有一個 json 配置文件,我想在其中存儲一個 json object 可以包含幾個未知字段。

我可以通過從這個基礎 class 派生,使用 Newtonsoft 反序列化這樣的 object:

public class ComplexJsonObject
{
    [JsonExtensionData]
    private IDictionary<string, JToken> _additionalData_newtonsoft;
}

不幸的是,配置文件appsettings.development.json似乎只是說我有一個空的 object。 即使配置了一些東西。

我認為這是因為系統使用了 System.Text.Json。 所以我也試過了:

public class ComplexJsonObject
{
    [JsonExtensionData]
    private IDictionary<string, JToken> _additionalData_newtonsoft;

    [System.Text.Json.Serialization.JsonExtensionData]
    [Newtonsoft.Json.JsonIgnore]
    public IDictionary<string, JsonElement> _additionalData_dotnet { get; set; }
}

這也不起作用。

那么問題來了:我如何告訴系統使用 Newtonsoft 來反序列化這個配置文件?

- 編輯 -

根據要求,我要存儲的配置示例。 配置鍵是“configuration:when”,我希望 object 必須有一個運算符,但所有其他字段都是動態的。

{
    "extraction": {
        "when": {
            "operator": "and",
            "rules": [
            {
                "operator": "or",
                "rules": [ 
                { "operator": "field.contains", "value": "waiting" },
                { "operator": "field.contains", "value": "approved" },
                { "operator": "field.contains", "value": "rejected" }
                ]
            },
            { "operator": "not", "rule": { "operator": "field.contains", "value": "imported" } },
            { "operator": "not", "rule": { "operator": "field.contains", "value": "import-failed" } }
            ]
        }
    }
}   

我認為 Métoule 是正確的,這確實是不可能的。 由於默認情況下配置會混合來自其他文件的值。

你想要什么是不可能的,因為這不是 .NET 配置系統的工作方式。 該配置不會直接將您的 JSON 解析為您的數據結構; 相反,它會創建一個中間表示(這是一個簡單的IDictionary<string,string> ),然后綁定到您的數據結構。

之所以不是直接映射,是因為配置數據可以來自多個來源。 例如,可以使用通過 Azure 門戶 UI 指定的值覆蓋 JSON 配置。 或者可能根本沒有 JSON 文件。

話雖如此,可能會濫用配置系統,就像我在以下問題中解釋的那樣:

如果您只需要使用 newtonsoft 轉換特定部分,則可以使用此擴展:

public static class ConfigurationBinder
{
    public static void BindJsonNet<T>(this IConfiguration config, T instance) where T : class
    {
        string objectPath = config.AsEnumerable().Select(x => x.Key).FirstOrDefault();

        var obj = BindToExpandoObject(config, objectPath);
        if (obj == null)
            return;

        var jsonText = JsonConvert.SerializeObject(obj);
        var jObj = JObject.Parse(jsonText);
        if (jObj == null)
            return;

        var jToken = jObj.SelectToken($"*.{GetLastObjectName(objectPath)}");
        if (jToken == null)
            return;

        jToken.Populate<T>(instance);
    }

    private static ExpandoObject BindToExpandoObject(IConfiguration config, string objectPath)
    {
        var result = new ExpandoObject();
        string lastObjectPath = GetLastObjectPath(objectPath);

        // retrieve all keys from your settings
        var configs = config.AsEnumerable();
        configs = configs
            .Select(x => new KeyValuePair<string, string>(x.Key.Replace(lastObjectPath, ""), x.Value))
            .ToArray();

        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 result;
    }

    private static string GetLastObjectPath(string objectPath)
    {
        string lastObjectPath = objectPath;
        int indexLastObj;
        if ((indexLastObj = objectPath.LastIndexOf(":")) != -1)
            lastObjectPath = objectPath.Remove(indexLastObj);
        return lastObjectPath;
    }

    private static string GetLastObjectName(string objectPath)
    {
        string lastObjectPath = objectPath;
        int indexLastObj;
        if ((indexLastObj = objectPath.LastIndexOf(":")) != -1)
            lastObjectPath = objectPath.Substring(indexLastObj + 1);
        return lastObjectPath;
    }

    private static 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;
            }

            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);
            }
        }
    }

    public static void Populate<T>(this JToken value, T target) where T : class
    {
        using (var sr = value.CreateReader())
        {
            JsonSerializer.CreateDefault().Populate(sr, target); // Uses the system default JsonSerializerSettings
        }
    }
}

用法:

var obj = new SampleObject();
Configuration.GetSection("test:arr:3:sampleObj").BindJsonNet(obj);
services.AddSingleton(obj);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM