目前 System.Text.Json 没有与 Json.NET 等效的选项MissingMemberHandling.Error https://www.newtonsoft.com/json/help/html/DeserializeMissingMemberHandling.htm当反序列化的 JSON 具有未映射的属性时强制发生错误的功能。如需确认,请参阅:
-
将 Newtonsoft.Json 与 System.Text.Json 进行比较,并迁移到 System.Text.Json https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0#missingmemberhandling:
如果 JSON 包含目标类型中缺少的属性,则可以将 Newtonsoft.Json 配置为在反序列化期间引发异常。 System.Text.Json 会忽略 JSON 中的额外属性,除非您使用[JsonExtensionData] 属性 https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/handle-overflow。对于缺少的成员功能没有解决方法。
-
添加对 MissingMemberHandling 的支持到 System.Text.Json #37483 https://github.com/dotnet/runtime/issues/37483.
但是,即使官方文档指出缺少成员功能没有解决方法,您也可以使用[JsonExtensionData]
要模拟的属性MissingMemberHandling.Error
.
首先,如果您只想实现几种类型MissingMemberHandling.Error
,您可以添加一个扩展数据字典,然后检查它是否包含内容并在JsonOnDeserialized.OnDeserialized() https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.ijsonondeserialized?view=net-7.0回调,或者按照建议在您的控制器中这个答案 https://stackoverflow.com/a/74995893/3744182 by 迈克尔·刘 https://stackoverflow.com/users/1127114/michael-liu.
其次,如果您需要实施MissingMemberHandling.Error
对于每种类型,在 .NET 7 及更高版本中,您可以添加DefaultJsonTypeInfoResolver 修饰符 https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/custom-contracts#modifiers添加一个合成扩展数据属性,该属性会在未知属性上引发错误。
为此,请定义以下扩展方法:
public static class JsonExtensions
{
public static DefaultJsonTypeInfoResolver AddMissingMemberHandlingError(this DefaultJsonTypeInfoResolver resolver)
{
resolver.Modifiers.Add(typeInfo =>
{
if (typeInfo.Kind != JsonTypeInfoKind.Object)
return;
if (typeInfo.Properties.Any(p => p.IsExtensionData))
return;
var property = typeInfo.CreateJsonPropertyInfo(typeof(Dictionary<string, JsonElement>), "<>ExtensionData");
property.IsExtensionData = true;
property.Get = static (obj) => null;
property.Set = static (obj, val) =>
{
var dictionary = (Dictionary<string, JsonElement>?)val;
Console.WriteLine(dictionary?.Count);
if (dictionary != null)
throw new JsonException();
};
typeInfo.Properties.Add(property);
});
return resolver;
}
}
然后按如下方式配置您的选项:
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
.AddMissingMemberHandlingError(),
};
这样做之后,一个JsonException
当遇到缺少的 JSON 属性时将抛出该异常。但请注意,Systen.Text.Json 在填充之前设置分配的字典,因此在使用此解决方法时,您将无法在异常消息中包含缺少的成员名称。
演示小提琴here https://dotnetfiddle.net/TiyGaM.
Update
如果您需要实施MissingMemberHandling.Error
对于每种类型and also需要异常错误消息包含未知属性的名称,可以通过定义自定义字典类型来完成,每当尝试向字典添加任何内容时,该类型都会引发自定义异常。然后使用该自定义字典类型作为合约修饰符添加的合成扩展属性中的扩展字典类型,如下所示:
// A JsonException subclass that allows for a custom message that includes the path, line number and byte position.
public class JsonMissingMemberException : JsonException
{
readonly string? innerMessage;
public JsonMissingMemberException() : this(null) { }
public JsonMissingMemberException(string? innerMessage) : base(innerMessage) => this.innerMessage = innerMessage;
public JsonMissingMemberException(string? innerMessage, Exception? innerException) : base(innerMessage, innerException) => this.innerMessage = innerMessage;
protected JsonMissingMemberException(SerializationInfo info, StreamingContext context) : base(info, context) => this.innerMessage = (string?)info.GetValue("innerMessage", typeof(string));
public override string Message =>
innerMessage == null
? base.Message
: String.Format("{0} Path: {1} | LineNumber: {2} | BytePositionInLine: {3}.", innerMessage, Path, LineNumber, BytePositionInLine);
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("innerMessage", innerMessage);
}
}
public static class JsonExtensions
{
class UnknownPropertyDictionary<TModel> : IDictionary<string, JsonElement>
{
static JsonException CreateException(string key, JsonElement value) =>
new JsonMissingMemberException(String.Format("Unexpected property \"{0}\" encountered while deserializing type {1}.", key, typeof(TModel).FullName));
public void Add(string key, JsonElement value) => throw CreateException(key, value);
public bool ContainsKey(string key) => false;
public ICollection<string> Keys => Array.Empty<string>();
public bool Remove(string key) => false;
public bool TryGetValue(string key, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out JsonElement value) { value = default; return false; }
public ICollection<JsonElement> Values => Array.Empty<JsonElement>();
public JsonElement this[string key]
{
get => throw new KeyNotFoundException(key);
set => throw CreateException(key, value);
}
public void Add(KeyValuePair<string, JsonElement> item) => throw CreateException(item.Key, item.Value);
public void Clear() => throw new NotImplementedException();
public bool Contains(KeyValuePair<string, JsonElement> item) => false;
public void CopyTo(KeyValuePair<string, JsonElement>[] array, int arrayIndex) { }
public int Count => 0;
public bool IsReadOnly => false;
public bool Remove(KeyValuePair<string, JsonElement> item) => false;
public IEnumerator<KeyValuePair<string, JsonElement>> GetEnumerator() => Enumerable.Empty<KeyValuePair<string, JsonElement>>().GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
public static DefaultJsonTypeInfoResolver AddMissingMemberHandlingError(this DefaultJsonTypeInfoResolver resolver)
{
resolver.Modifiers.Add(typeInfo =>
{
if (typeInfo.Kind != JsonTypeInfoKind.Object)
return;
if (typeInfo.Properties.Any(p => p.IsExtensionData))
return;
var dictionaryType = typeof(UnknownPropertyDictionary<>).MakeGenericType(typeInfo.Type);
JsonPropertyInfo property = typeInfo.CreateJsonPropertyInfo(dictionaryType, "<>ExtensionData");
property.IsExtensionData = true;
property.Get = (obj) => Activator.CreateInstance(dictionaryType);
property.Set = static (obj, val) => { };
typeInfo.Properties.Add(property);
});
return resolver;
}
}
然后,如果我尝试将具有未知属性的 JSON 反序列化为不包含该属性的模型,则会引发以下异常:
JsonMissingMemberException: Unexpected property "Unknown" encountered while deserializing type Model. Path: $.Unknown | LineNumber: 6 | BytePositionInLine: 16.
at JsonExtensions.UnknownPropertyDictionary`1.set_Item(String key, JsonElement value)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo.ReadJsonAndAddExtensionProperty(Object obj, ReadStack& state, Utf8JsonReader& reader)
Notes:
Demo #2 here https://dotnetfiddle.net/DbmJfT.