我正在嘗試使用 Newtonsoft.Json 序列化和反序列化Dictionary<(int, int), MyClass>. 因為(int, int)必須序列化為字串,所以我必須提供一個自定義TypeConverter以將其反序列化回元組:
public class Tuple2Converter<T1, T2> : TypeConverter {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
var parts = Convert.ToString(value).Trim('(').Trim(')').Split(", ");
var item1 = (T1)TypeDescriptor.GetConverter(typeof(T1)).ConvertFromInvariantString(parts[0])!;
var item2 = (T2)TypeDescriptor.GetConverter(typeof(T2)).ConvertFromInvariantString(parts[1])!;
return (item1, item2);
}
}
// ...
TypeDescriptor.AddAttributes(typeof((int, int)), new TypeConverterAttribute(typeof(Tuple2Converter<int, int>)));
var resultingObject =
JsonConvert.DeserializeObject<Dictionary<(int Q, int R), HexWithMeta>>(dictString);
但是,在反序列化時,我現在收到錯誤:
無法將當前 JSON 物件(例如 {"name":"value"})反序列化為 'System.ValueTuple`2[System.Int32,System.Int32]' 型別,因為該型別需要 JSON 字串值才能正確反序列化。要修復此錯誤,請將 JSON 更改為 JSON 字串值或將反序列化型別更改為可以反序列化的普通 .NET 型別(例如,不是整數等原始型別,而不是陣列或串列等集合型別)來自 JSON 物件。JsonObjectAttribute 也可以添加到型別中以強制它從 JSON 物件反序列化。
它正在嘗試使用自定義 TypeConverter 將另一個轉換(int, int)為 C# 元組,但是這一次,元組以標準方式序列化(JSON 物件而不是字串),因為這個元組是存在于 上的MyClass,所以它被序列化為:
"QR": {
"Item1": 0,
"Item2": 0
}
在反序列化 Dictionary 鍵上的字串編碼元組時,如何讓 Newtonsoft.Json 使用自定義 TypeConverter,但不能用于 Dictionary 的序列化值中包含的任何元組?
請注意,我只是通過全域系結我的 TypeConverterTypeDescriptor.AddAttributes()以獲得正確的 JSON 序列化,出于其他原因我不需要這樣做。
uj5u.com熱心網友回復:
即使使用自定義合同決議器,Json.NET 也沒有一種方便的方法來注入自定義方法來將字典鍵從 JSON 屬性名稱轉換為 JSON 屬性名稱。[1] 全域系結TypeConverter是將 JSON 屬性名稱系結到復雜字典鍵型別的記錄方法。但是,正如您所發現的,在自定義TypeConverter中為復雜型別系結也會導致該型別在獨立序列化以及格式化為字典鍵時序列化為字串。
因此,如果您想為特定的字典鍵型別(此處ValueTuple<int, int>)使用自定義字串轉換邏輯,但在獨立序列化該型別時不使用相同的邏輯,您有以下選項:
您可以像當前一樣系結全域,然后通過自定義或合同決議器
TypeConverter取消使用,如對Newtonsoft.JSON cannot convert model with TypeConverter attribute的答案所示。TypeConverterJsonConverter您可以跳過使用
TypeConverter并為您的.JsonConverterDictionary<(int, int), MyClass>
由于系結TypeConverter全域 viaTypeDescriptor.AddAttributes可能會產生意想不到的副作用,而且您也不是特別想這樣做,因此我建議使用自定義JsonConverter方法。這是一個有效的方法:
public class DictionaryTupleConverter<T1, T2, TValue> : JsonConverter<Dictionary<(T1, T2), TValue>>
{
readonly TypeConverter converter1 = TypeDescriptor.GetConverter(typeof(T1)); // Cache for performance
readonly TypeConverter converter2 = TypeDescriptor.GetConverter(typeof(T2));
public override Dictionary<(T1, T2), TValue>? ReadJson(JsonReader reader, Type objectType, Dictionary<(T1, T2), TValue>? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var innerDictionary = serializer.Deserialize<Dictionary<string, TValue>>(reader);
if (innerDictionary == null)
return null;
var dictionary = existingValue ?? (Dictionary<(T1, T2), TValue>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator!();
foreach (var pair in innerDictionary)
dictionary.Add(ConvertFrom(pair.Key), pair.Value);
return dictionary;
}
public override void WriteJson(JsonWriter writer, Dictionary<(T1, T2), TValue>? value, JsonSerializer serializer) =>
serializer.Serialize(writer, value!.ToDictionary(p => ConvertTo(p.Key)!, p => p.Value));
(T1, T2) ConvertFrom(string value)
{
var parts = value.Trim('(').Trim(')').Split(",");
var item1 = (T1)converter1.ConvertFromInvariantString(parts[0].Trim())!;
var item2 = (T2)converter2.ConvertFromInvariantString(parts[1].Trim())!;
return (item1, item2);
}
string ConvertTo((T1, T2) value)
{
var s1 = converter1.ConvertToInvariantString(value.Item1)!;
var s2 = converter2.ConvertToInvariantString(value.Item2)!;
return string.Format("({0},{1})", s1, s2);
}
}
然后使用以下設定進行序列化和反序列化:
var settings = new JsonSerializerSettings
{
Converters = { new DictionaryTupleConverter<int, int, HexWithMeta>() },
};
筆記:
我不確定是否
ValueTuple<T1, T2>.ToString()是區域設定不變的(這個github 問題建議不是),所以我建議創建自己的方法來將元組轉換為可驗證的區域設定不變的字串。您的 from 和 to 字串的轉換方法
(T1, T2)沒有考慮內部字串可能包含逗號或括號的可能性。您可能希望使用某種簡單的 CSV 決議器來增強決議和格式化邏輯。
演示小提琴在這里。
[1]為確認您可以查看 的源代碼JsonSerializerInternalWriter.GetPropertyName(),該方法用于從字典鍵生成 JSON 屬性名稱。該方法不是虛擬的,而是作為一系列硬編碼檢查實作的,沒有注入自定義邏輯的選項。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/522833.html
