[英]Fail to serialize IConfigurationSection from Json
我有以下基於Json的配置文件:
{
"PostProcessing": {
"ValidationHandlerConfiguration": {
"MinimumTrustLevel": 80,
"MinimumMatchingTrustLevel": 75
},
"MatchingCharacterRemovals": [
"-",
"''",
":"
]
},
"Processing": {
"OrderSelection": {
"SelectionDaysInterval": 30,
"SelectionDaysMaximum": 365
}
}
}
作為序列化框架,我使用Newtonsoft 。 要將此配置序列化為對象,我已實現以下類:
[JsonObject(MemberSerialization.OptIn)]
public class RecognitionConfiguration {
[JsonProperty(PropertyName = "PostProcessing", Required = Required.Always)]
public PostRecognitionConfiguration PostRecognitionConfiguration { get; set; }
[JsonProperty(PropertyName = "Processing", Required = Required.Always)]
public ProcessRecognitionConfiguration ProcessRecognitionConfiguration { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class PostRecognitionConfiguration {
[JsonProperty(Required = Required.Always)]
public ValidationHandlerConfiguration ValidationHandlerConfiguration { get; set; }
[JsonProperty] public List<string> MatchingCharacterRemovals { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class ProcessRecognitionConfiguration {
[JsonProperty(PropertyName = "OrderSelection", Required = Required.Always)]
public OrderSelectionConfiguration OrderSelectionConfiguration { get; set; }
}
在類中,我嘗試使用IConfigurationSection.Get()將特定配置節序列化為這些類結構。
var serializedConfiguration = this.ConfigurationSection.Get<RecognitionConfiguration>();
但是當我調試代碼時,我總是得到一個“空”變量serializedConfiguration ,它不是null,但是所有屬性都是null。
如果我使用
this.ConfigurationSection.GetSection("Processing").Get<ProcessRecognitionConfiguration>()
或者更改json文件中屬性的命名,使其與類中的屬性名稱完全匹配,如下所示:
{
"ProcessRecognitionConfiguration": {
"OrderSelectionConfiguration": {
"SelectionDaysInterval": 30,
"SelectionDaysMaximum": 365
}
}
}
它工作正常。 你有什么想法,為什么在JsonProperty上設置PropertyName似乎沒有任何影響?
那是設計上的。 通過配置綁定到POCO是按照慣例完成的。 不像模型綁定到控制器動作參數。
它將POCO上的屬性名稱與提供的JSON中的鍵進行匹配。
因此,要么更改設置以匹配原始問題中顯示的類,要么更改類以匹配基於Json的配置文件中的設置鍵。
[JsonObject(MemberSerialization.OptIn)]
public class RecognitionConfiguration {
[JsonProperty(PropertyName = "PostProcessing", Required = Required.Always)]
public PostRecognitionConfiguration PostProcessing{ get; set; }
[JsonProperty(PropertyName = "Processing", Required = Required.Always)]
public ProcessRecognitionConfiguration Processing{ get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class PostRecognitionConfiguration {
[JsonProperty(Required = Required.Always)]
public ValidationHandlerConfiguration ValidationHandlerConfiguration { get; set; }
[JsonProperty]
public List<string> MatchingCharacterRemovals { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class ProcessRecognitionConfiguration {
[JsonProperty(PropertyName = "OrderSelection", Required = Required.Always)]
public OrderSelectionConfiguration OrderSelection { get; set; }
}
public partial class ValidationHandlerConfiguration {
[JsonProperty("MinimumTrustLevel")]
public long MinimumTrustLevel { get; set; }
[JsonProperty("MinimumMatchingTrustLevel")]
public long MinimumMatchingTrustLevel { get; set; }
}
public partial class OrderSelectionConfiguration {
[JsonProperty("SelectionDaysInterval")]
public long SelectionDaysInterval { get; set; }
[JsonProperty("SelectionDaysMaximum")]
public long SelectionDaysMaximum { get; set; }
}
編輯:我發現這個比以前的解決方案更令人愉快:將所有內容綁定到ExpandoObject
,將它們寫入JSON並使用JSON.NET將它們綁定回來。 使用本文的代碼 :
namespace Microsoft.Extensions.Configuration
{
public static class ConfigurationBinder
{
public static void BindJsonNet(this IConfiguration config, object instance)
{
var obj = BindToExpandoObject(config);
var jsonText = JsonConvert.SerializeObject(obj);
JsonConvert.PopulateObject(jsonText, instance);
}
private static ExpandoObject BindToExpandoObject(IConfiguration config)
{
var result = new ExpandoObject();
// retrieve all keys from your settings
var configs = config.AsEnumerable();
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 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);
}
}
}
}
}
用法:
var settings = new MySettings();
this.Configuration.BindJsonNet(settings);
這是我的測試MySettings
類:
public class MySettings
{
[JsonProperty("PostProcessing")]
public SomeNameElseSettings SomenameElse { get; set; }
public class SomeNameElseSettings
{
[JsonProperty("ValidationHandlerConfiguration")]
public ValidationHandlerConfigurationSettings WhateverNameYouWant { get; set; }
public class ValidationHandlerConfigurationSettings
{
[JsonProperty("MinimumTrustLevel")]
public int MinimumTrustLevelFoo { get; set; }
[JsonProperty("MinimumMatchingTrustLevel")]
public int MinimumMatchingTrustLevelBar { get; set; }
}
}
}
通話結束后,我按照你的意願得到了一切:
舊答案:
根據這里的源代碼 ,只需(幾乎)不可能完成您的要求。 我已經嘗試了JsonProperty
和DataContract
,其中沒有一個被Binder尊重,只是因為源代碼本身只使用屬性名稱。
如果您仍然堅持,有兩種可能性,但我不建議任何更改屬性的名稱更簡單:
在那里分叉您的源代碼,或者只是復制該文件(在我嘗試跟蹤代碼時,我將所有方法重命名為類似Bind2
, BindInstance2
等),並相應地重寫代碼。
這個是非常具體的當前實現,因此它不是面向未來的:當前代碼調用config.GetSection(property.Name)
,因此您可以編寫自己的IConfiguration
並為GetSection
方法提供自己的名稱並將其點擊到引導過程而不是使用默認過程。
在JsonProperty
上更改PropertyName
確實有效。 這是我嘗試的相同,它確實對我有用:
我的JSON數據:
{"name": "John","age": 30,"cars": [ "Ford", "BMW", "Fiat" ]}
和模型:
public class RootObject
{
[JsonProperty(PropertyName ="name")]
public string Apple { get; set; }
public int age { get; set; }
public List<string> cars { get; set; }
}
這是代碼:
RootObject obj = JsonConvert.DeserializeObject<RootObject>(json);
這是我得到的輸出
您需要在JsonProperty
設置PropertyName
與json文件屬性名稱相同,但您的C#模型屬性可以是您想要的,只需要用[JsonProperty(PropertyName ="jsonPropertyName")]
希望這有助於您解決問題。
快樂的編碼......
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.