我有一个asp.net核心webservice,需要正确处理对象的接口属性(必须找出用于反序列化json-string的实现)。
因为这个原因,我创建了一个从接口 - 属性调用的类,如:
[JsonConverter(typeof(InterfaceJsonConverter<IInputFormat>))]
public interface IInputFormat
{
// ...
}
定义InterfaceJsonConverter的模型类必须是.NET Standard 1.4,它不支持程序集加载。 但是这种技术用于找出哪个程序集具有接口的实现以及哪个实现最接近json-object。 (我当然可以直接将接口添加到类而不是读取程序集,但我想要一个我永远不会错过实现的动态解决方案)
(最初我是在.net 4.6中开发的,它在那里工作得非常好。)
所以我现在要做的是:
有没有办法存档? 或者有更好的方法来处理asp.net核心中的接口吗?
为了完整性,我将发布InterfaceJsonConverter的代码
public class InterfaceJsonConverter<T> : Newtonsoft.Json.JsonConverter
{
public InterfaceJsonConverter()
{
this.DerivedTypes = GetTypesOfImplementedInterfaces(typeof(T));
}
readonly HashSet<Type> derivedTypes = new HashSet<Type>();
public InterfaceJsonConverter(params Type[] types)
{
this.DerivedTypes = types;
}
public InterfaceJsonConverter(System.Reflection.Assembly assembly, string @namespace)
{
this.DerivedTypes = GetTypesInNamespace(assembly, @namespace);
}
private Type[] GetTypesOfImplementedInterfaces(Type type)
{
var parts = type.AssemblyQualifiedName.Split(',');
var assemblyName = parts[1].Trim();
var assemblies = AssemblyRepo.GetReferencingAssemblies(assemblyName);
//var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var types = new List<Type>();
foreach (var a in assemblies)
{
try
{
//type.GetInterfaces().Any(i => i.FullName == typeof(T).FullName)
var currentTypes = a.GetTypes().Where(t => t.GetTypeInfo().IsAbstract==false && t.GetInterfaces().Any(i => i.FullName == type.FullName)).ToList();
types.AddRange(currentTypes);
}
catch (System.Exception) { }// ignored}
}
if(types.Count==0)
throw new System.Exception("No class found which implements interface [" + typeof(T) + "].");
return types.ToArray();
}
private Type[] GetTypesInNamespace(System.Reflection.Assembly assembly, string @namespace)
{
//return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray();
return assembly.GetTypes()
.Where(
type => String.Equals(type.Namespace, @namespace)
&& type.GetInterfaces().Any(i => i.FullName == typeof (T).FullName)
).ToArray();
}
public IEnumerable<Type> DerivedTypes
{
get
{
return derivedTypes.ToArray();
}
set
{
if (value == null)
throw new ArgumentNullException();
derivedTypes.Clear();
derivedTypes.UnionWith(value);
}
}
JsonObjectContract FindContract(JObject obj, JsonSerializer serializer)
{
List<JsonObjectContract> bestContracts = new List<JsonObjectContract>();
foreach (var type in derivedTypes)
{
if (type.GetTypeInfo().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.Count > 0 ? bestContracts.Single() : null;
}
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);
}
}
var contract = FindContract(obj, serializer);
if (contract == null)
throw new JsonSerializationException("no contract found for " + obj.ToString());
if (existingValue == null || !contract.UnderlyingType.IsInstanceOfType(existingValue))
//!contract.UnderlyingType.IsAssignableFrom(existingValue.GetType()))
existingValue = contract.DefaultCreator();
//will call the default constructor(PARAMETERLESS)...MAKE SURE YOUR CLASS HAS ONE!!!!!
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();
}
}