Tob*_*ler 5 c# json asp.net-web-api2
我有一个来自客户端并自动从 Web Api 2 反序列化的对象。
现在我的模型的一个属性有问题。此属性“CurrentField”属于 IField 类型,此接口有 2 种不同的实现。
这是我的模型(只是一个假人)
public class MyTest
{
public IField CurrentField {get;set;}
}
public interface IField{
string Name {get;set;}
}
public Field1 : IField{
public string Name {get;set;}
public int MyValue {get;set;}
}
public Field2 : IField{
public string Name {get;set;}
public string MyStringValue {get;set;}
}
Run Code Online (Sandbox Code Playgroud)
我试图创建一个自定义 JsonConverter 来找出我的客户端对象是什么类型(Field1 或 Field2),但我不知道如何。
我的转换器被调用,当我调用 var obj = JObject.load(reader); 时我可以看到对象。
但我怎样才能知道它是什么类型呢?我不能做类似的事情
if(obj is Field1) ...
Run Code Online (Sandbox Code Playgroud)
这是我应该检查的方法吗?
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
Run Code Online (Sandbox Code Playgroud)
使用 Json.NET 反序列化接口时如何自动选择具体类型
解决问题的最简单方法是使用 .json 序列化和反序列化 JSON(在客户端和服务器端)TypeNameHandling = TypeNameHandling.Auto。如果这样做,您的 JSON 将包含为IFIeld属性序列化的实际类型,如下所示:
Run Code Online (Sandbox Code Playgroud){ "CurrentField": { "$type": "MyNamespace.Field2, MyAssembly", "Name": "name", "MyStringValue": "my string value" } }
但是,请注意Newtonsoft 文档中的这一警告:
当您的应用程序从外部源反序列化 JSON 时,应谨慎使用 TypeNameHandling。当使用 None 以外的值反序列化时,应使用自定义 SerializationBinder 验证传入类型。
有关为什么需要这样做的讨论,请参阅Newtonsoft Json 中的 TypeNameHandling 警告、如何配置 Json.NET 以创建易受攻击的 Web API以及 Alvaro Muñoz 和 Oleksandr Mirosh 的黑帽论文https://www.blackhat.com/docs/ us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf
如果由于某种原因您无法更改服务器输出的内容,您可以创建一个JsonConverter将 JSON 加载到 aJObject并检查以查看实际存在的字段,然后搜索可能的具体类型以找到具有相同属性的类型:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
然后您可以将其作为转换器应用于IField:
[JsonConverter(typeof(JsonDerivedTypeConverer<IField>), new object [] { new Type [] { typeof(Field1), typeof(Field2) } })]
public interface IField
{
string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
请注意,此解决方案有点脆弱。如果服务器省略了MyStringValueorMyValue字段(DefaultValueHandling = DefaultValueHandling.Ignore例如,因为它们具有默认值 and ),那么转换器将不知道要创建哪种类型并抛出异常。类似地,如果实现的两个具体类型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();
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4547 次 |
| 最近记录: |