[英]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.