我有一個具有以下Dictionary<string, string>
屬性的資料模型:
public class Model
{
public string Name { get; set; }
public Dictionary<string, string> Attributes { get; set; }
}
在某些極少數情況下,我會收到帶有重復屬性名稱的 JSON Attributes
,例如:
{
"name":"Object Name",
"attributes":{
"key1":"adfadfd",
"key1":"adfadfadf"
}
}
我希望在這種情況下引發例外,但是當我使用 Json.NET 反序列化時沒有錯誤,而字典包含最后遇到的值。在這種情況下如何強制出錯?
作為一種解決方法,我目前將屬性宣告為鍵/值對串列:
public List<KeyValuePair<string, string>> Attributes { get; set;
這需要我以以下格式序列化屬性:
"attributes": [
{
"key": "key1",
"value": "adfadfd"
},
{
"key": "key1",
"value": "adfadfadf"
}
]
然后稍后我可以檢測到重復項。但是,我更喜歡使用更緊湊的 JSON 物件語法而不是 JSON 陣列語法,并將 Attributes 宣告為字典。
uj5u.com熱心網友回復:
似乎,當從具有重復屬性名稱的 JSON 物件反序列化字典時,Json.NET(以及 System.Text.Json)默默地用最后一個重復鍵的值填充字典。(此處演示。)這并不完全令人驚訝,因為 JSON RFC 8259指出:
當物件中的名稱不唯一時,接收此類物件的軟體的行為是不可預測的。許多實作僅報告姓氏/值對...
由于您不希望這樣,您可以創建一個自定義 JsonConverter,在屬性名稱重復的情況下引發錯誤:
public class NoDuplicateKeysDictionaryConverter<TValue> : NoDuplicateKeysDictionaryConverter<Dictionary<string, TValue>, TValue>
{
}
public class NoDuplicateKeysDictionaryConverter<TDictionary, TValue> : JsonConverter<TDictionary> where TDictionary : IDictionary<string, TValue>
{
public override TDictionary ReadJson(JsonReader reader, Type objectType, TDictionary existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
return typeof(TDictionary).IsValueType && Nullable.GetUnderlyingType(typeof(TDictionary)) == null ? throw new JsonSerializationException("null value") : default;
reader.AssertTokenType(JsonToken.StartObject);
var dictionary = existingValue ?? (TDictionary)serializer.ContractResolver.ResolveContract(typeof(TDictionary)).DefaultCreator();
// Todo: decide whether you want to clear the incoming dictionary.
while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndObject)
{
var key = (string)reader.AssertTokenType(JsonToken.PropertyName).Value;
var value = serializer.Deserialize<TValue>(reader.ReadToContentAndAssert());
// https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.idictionary-2.add#exceptions
// Add() will throw an ArgumentException when an element with the same key already exists in the IDictionary<TKey,TValue>.
dictionary.Add(key, value);
}
return dictionary;
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, TDictionary value, JsonSerializer serializer) => throw new NotImplementedException();
}
public static partial class JsonExtensions
{
public static JsonReader AssertTokenType(this JsonReader reader, JsonToken tokenType) =>
reader.TokenType == tokenType ? reader : throw new JsonSerializationException(string.Format("Unexpected token {0}, expected {1}", reader.TokenType, tokenType));
public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
reader.ReadAndAssert().MoveToContentAndAssert();
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
然后將其添加到您的模型中,如下所示:
[Newtonsoft.Json.JsonConverter(typeof(NoDuplicateKeysDictionaryConverter<string>))]
public Dictionary<string, string> Attributes { get; set; }
每當ArgumentException
嘗試將重復的鍵添加到字典中時,都會拋出 an 。
演示在這里。
uj5u.com熱心網友回復:
查看Json.NET的源代碼,代碼就是這樣做的:
dictionary[keyValue] = itemValue;
因此,一種選擇是撰寫一個包裝器來Dictionary
提供您想要的功能。我們可以通過所有呼叫,除了索引器,它會通過 toAdd
而不是會導致例外。
從技術上講,Json.NET 代碼只要求一個
IDictionary
,而不是一個,IDictionary<TKey, TValue>
但是如果沒有強制轉換和/或拆箱,您將無法從中讀取。
const string json =@"
{
""name"":""Object Name"",
""attributes"":{
""key1"":""adfadfd"",
""key1"":""adfadfadf""
}
}
";
Console.WriteLine(JsonConvert.DeserializeObject<Model>(json));
public class Model
{
public string Name { get; set; }
public StrictDictionary<string, string> Attributes { get; set; }
}
public class StrictDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
public Dictionary<TKey, TValue> InnerDictionary {get; set; } = new Dictionary<TKey, TValue>();
public bool ContainsKey(TKey key) => InnerDictionary.ContainsKey(key);
public void Add(TKey key, TValue value) => InnerDictionary.Add(key, value);
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> kvp) => ((ICollection<KeyValuePair<TKey, TValue>>) InnerDictionary).Add(kvp);
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> kvp) => ((ICollection<KeyValuePair<TKey, TValue>>) InnerDictionary).Contains(kvp);
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int i) => ((ICollection<KeyValuePair<TKey, TValue>>) InnerDictionary).CopyTo(array, i);
public void Clear() => InnerDictionary.Clear();
public bool Remove(TKey key) => InnerDictionary.Remove(key);
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> kvp) => ((ICollection<KeyValuePair<TKey, TValue>>) InnerDictionary).Remove(kvp);
public bool TryGetValue(TKey key, out TValue value) => InnerDictionary.TryGetValue(key, out value);
public ICollection<TKey> Keys => InnerDictionary.Keys;
public ICollection<TValue> Values => InnerDictionary.Values;
public int Count => InnerDictionary.Count;
public bool IsReadOnly => ((ICollection<KeyValuePair<TKey, TValue>>) InnerDictionary).IsReadOnly;
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => InnerDictionary.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => InnerDictionary.GetEnumerator();
public TValue this[TKey key]
{
get => InnerDictionary[key];
set => InnerDictionary.Add(key, value);
}
}
dotnetfiddle
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/470164.html