带接口的JsonConverter

带接口的JsonConverter,第1张

带接口的JsonConverter

使用Json.NET反序列化接口时如何自动选择具体类型

解决问题的最简单方法是使用序列化和反序列化JSON(在客户端和服务器端)

TypeNameHandling =TypeNameHandling.Auto
。如果这样做,您的JSON将包括为
IFIeld
属性序列化的实际类型,如下所示:

{  "CurrentField": {    "$type": "MyNamespace.Field2, MyAssembly",    "Name": "name",    "MyStringValue": "my string value"  }}

但是,请注意Newtonsoft文档中的这一警告:

当您的应用程序从外部源反序列化JSON时,应谨慎使用TypeNameHandling。反序列化除None以外的其他值时,应使用自定义SerializationBinder验证传入的类型。

有关为什么这样做的必要性的讨论,请参阅Newtonsoft
Json中的TypeNameHandling警告,如何配置Json.NET以创建易受攻击的Web
API
,以及AlvaroMuñoz和Oleksandr
Mirosh的blackhat论文https://www.blackhat.com/docs/我们17 /周四/us-17-Munoz-Friday-
The-13th-JSON-Attacks-
wp.pdf

如果出于某种原因无法更改服务器输出,则可以创建一个

JsonConverter
将JSON加载到中
JObject
并检查实际存在的字段,然后搜索可能的具体类型以找到具有相同属性的字段:

public class JsonDerivedTypeConverer<T> : JsonConverter{    public JsonDerivedTypeConverer() { }    public JsonDerivedTypeConverer(params Type[] types)    {        this.DerivedTypes = types;    }    readonly HashSet<Type> derivedTypes = new HashSet<Type>();    public IEnumerable<Type> DerivedTypes    {        get        { return derivedTypes.ToArray();         }        set        { if (value == null)     throw new ArgumentNullException(); derivedTypes.Clear(); if (value != null)     derivedTypes.UnionWith(value);        }    }    JsonObjectContract FindContract(JObject obj, JsonSerializer serializer)    {        List<JsonObjectContract> bestContracts = new List<JsonObjectContract>();        foreach (var type in derivedTypes)        { if (type.IsAbstract)     continue; var contract = serializer.ContractResolver.ResolveContract(type) as JsonObjectContract; if (contract == null)     continue; if (obj.Properties().Select(p => p.Name).Any(n => contract.Properties.GetClosestMatchProperty(n) == null))     continue; if (bestContracts.Count == 0 || bestContracts[0].Properties.Count > contract.Properties.Count) {     bestContracts.Clear();     bestContracts.Add(contract); } else if (contract.Properties.Count == bestContracts[0].Properties.Count) {     bestContracts.Add(contract); }        }        return bestContracts.Single();    }    public override bool CanConvert(Type objectType)    {        return objectType == typeof(T);    }    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)    {        if (reader.TokenType == JsonToken.Null) return null;        var obj = JObject.Load(reader); // Throws an exception if the current token is not an object.        var contract = FindContract(obj, serializer);        if (contract == null) throw new JsonSerializationException("no contract found for " + obj.ToString());        if (existingValue == null || !contract.UnderlyingType.IsAssignableFrom(existingValue.GetType())) existingValue = contract.DefaultCreator();        using (var sr = obj.CreateReader())        { serializer.Populate(sr, existingValue);        }        return existingValue;    }    public override bool CanWrite { get { return false; } }    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)    {        throw new NotImplementedException();    }}

然后,您可以将其用作转换器

IField

[JsonConverter(typeof(JsonDerivedTypeConverer<IField>), new object [] { new Type [] { typeof(Field1), typeof(Field2) } })]public interface IField{    string Name { get; set; }}

请注意,此解决方案有些脆弱。如果服务器省略

MyStringValue
or
MyValue
字段(
DefaultValueHandling =DefaultValueHandling.Ignore
例如,因为它们具有默认值和),则转换器将不知道要创建哪种类型,并且将引发异常。同样,如果两个实现的具体类型
IField
具有相同的属性名称,但类型不同,则转换器将引发异常。使用
TypeNameHandling.Auto
可以避免这些潜在的问题。

更新资料

以下版本检查该

"$type"
参数是否存在,如果
TypeNameHandling !=TypeNameHandling.None
,则返回默认序列化。它必须采取一些技巧来防止回退时无限递归:

public class JsonDerivedTypeConverer<T> : JsonConverter{    public JsonDerivedTypeConverer() { }    public JsonDerivedTypeConverer(params Type[] types)    {        this.DerivedTypes = types;    }    readonly HashSet<Type> derivedTypes = new HashSet<Type>();    public IEnumerable<Type> DerivedTypes    {        get        { return derivedTypes.ToArray();         }        set        { derivedTypes.Clear(); if (value != null)     derivedTypes.UnionWith(value);        }    }    JsonObjectContract FindContract(JObject obj, JsonSerializer serializer)    {        List<JsonObjectContract> bestContracts = new List<JsonObjectContract>();        foreach (var type in derivedTypes)        { if (type.IsAbstract)     continue; var contract = serializer.ContractResolver.ResolveContract(type) as JsonObjectContract; if (contract == null)     continue; if (obj.Properties().Select(p => p.Name).Where(n => n != "$type").Any(n => contract.Properties.GetClosestMatchProperty(n) == null))     continue; if (bestContracts.Count == 0 || bestContracts[0].Properties.Count > contract.Properties.Count) {     bestContracts.Clear();     bestContracts.Add(contract); } else if (contract.Properties.Count == bestContracts[0].Properties.Count) {     bestContracts.Add(contract); }        }        return bestContracts.Single();    }    public override bool CanConvert(Type objectType)    {        return objectType == typeof(T);    }    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)    {        if (reader.TokenType == JsonToken.Null) return null;        var obj = JObject.Load(reader); // Throws an exception if the current token is not an object.        if (obj["$type"] != null && serializer.TypeNameHandling != TypeNameHandling.None)        { // Prevent infinite recursion when using an explicit converter in the list. var removed = serializer.Converters.Remove(this); try {     // Kludge to prevent infinite recursion when using JsonConverterAttribute on the type: deserialize to object.     return obj.ToObject(typeof(object), serializer); } finally {     if (removed)         serializer.Converters.Add(this); }        }        else        { var contract = FindContract(obj, serializer); if (contract == null)     throw new JsonSerializationException("no contract found for " + obj.ToString()); if (existingValue == null || !contract.UnderlyingType.IsAssignableFrom(existingValue.GetType()))     existingValue = contract.DefaultCreator(); using (var sr = obj.CreateReader()) {     serializer.Populate(sr, existingValue); } return existingValue;        }    }    public override bool CanWrite { get { return false; } }    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)    {        throw new NotImplementedException();    }}


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5174587.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-18
下一篇 2022-11-18

发表评论

登录后才能评论

评论列表(0条)

保存