带接口的JsonConverter

我有一个来自客户端的对象,并自动从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;}

}

我试图创建一个自定义的JsonConverter来查找来自客户端的对象是什么类型(Field1或Field2),但是我只是不知道如何。

我的Converter被调用,当我调用var obj = JObject.load(reader);时,我可以看到该对象。

但是我如何找出它是什么类型?我不能做这样的事情

if(obj is Field1) ...

这是我应该检查此方法的方法吗?

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)

回答:

解决问题的最简单方法是使用序列化和反序列化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; }

}

请注意,此解决方案有些脆弱。如果服务器省略MyStringValueor 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();

}

}

以上是 带接口的JsonConverter 的全部内容, 来源链接: utcz.com/qa/412709.html

回到顶部