我有一个泛型类,它包含一个公共属性,它是与父类相同类型的泛型接口。下面的示例代码。
public interface IExample<T>
{
T Value { get; set; }
string Name { get; set; }
}
public class Example<T> : IExample<T>
{
public string Name { get; set; }
public T Value { get; set; }
}
public class Parent<T>
{
public string ParentName { get; set; }
public IExample<T> ExampleItem { get; set; }
}
public class MainClass
{
public Parent<int> IntParent { get; set; }
}
我正在使用JSON.net来序列化MainClass
对象,该对象可以包含许多Parent<T>
个对象。 Parent<T>
可以是没有类型约束的任何泛型。但是,我似乎无法以通用的方式反序化生成的JSON。
我试图为JSON.net反序列化器创建一个JsonConverter
,但我找不到一种通用的方法。示例JsonConverter
如下。
public class InterfaceJsonConverter<TInterface, TImplementation> : JsonConverter where TImplementation : TInterface, new()
{
public override bool CanConvert(Type objectType)
{
return (typeof(TInterface) == objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<TImplementation>(reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
上述转换器允许将属性放在ExampleItem
类的Parent<T>
属性上,如:
public class Parent<T>
{
public string ParentName { get; set; }
[JsonConverter(typeof(InterfaceJsonConverter<IExample<T>, Example<T>>))]
public IExample<T> ExampleItem { get; set; }
}
但是c#不允许在属性中使用泛型类型引用(因为属性和反射的性质)。到目前为止,我提出的唯一解决方案是在调用InterfaceJsonConverter
方法之前,手动将每个预期类型T的新Deserialize()
添加到序列化程序。但是,这限制了Parent<T>
的可能类型,因为如果可以反序列化,则需要手动添加每种类型。
有没有办法以通用方式反序列化?我应该采取另一种方法吗?
答案 0 :(得分:3)
这可以通过将open generic type typeof(Example<>)
作为构造函数参数传递给适当的JsonConverter
,然后在ReadJson()
内构建适当的封闭泛型类型来间接完成假设传入的objectType
具有与所需的具体封闭泛型类型相同的通用参数。
另请注意,只要转换器使用[JsonConverter(Type,Object[])]
直接应用于属性,转换器就不必知道接口类型,因为不会调用CanConvert()
。仅当转换器位于JsonSerializer.Converters
列表中时才会调用CanConvert()
。
因此您的转换器变为:
public class InterfaceToConcreteGenericJsonConverter : JsonConverter
{
readonly Type GenericTypeDefinition;
public InterfaceToConcreteGenericJsonConverter(Type genericTypeDefinition)
{
if (genericTypeDefinition == null)
throw new ArgumentNullException();
this.GenericTypeDefinition = genericTypeDefinition;
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
Type MakeGenericType(Type objectType)
{
if (!GenericTypeDefinition.IsGenericTypeDefinition)
return GenericTypeDefinition;
try
{
var parameters = objectType.GetGenericArguments();
return GenericTypeDefinition.MakeGenericType(parameters);
}
catch (Exception ex)
{
// Wrap the reflection exception in something more useful.
throw new JsonSerializationException(string.Format("Unable to construct concrete type from generic {0} and desired type {1}", GenericTypeDefinition, objectType), ex);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize(reader, MakeGenericType(objectType));
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
按如下方式应用:
public class Parent<T>
{
public string ParentName { get; set; }
[JsonConverter(typeof(InterfaceToConcreteGenericJsonConverter), new object[] { typeof(Example<>) })]
public IExample<T> ExampleItem { get; set; }
}
要将带参数的转换器应用于集合项,请使用JsonPropertyAttribute.ItemConverterType
和JsonPropertyAttribute.ItemConverterParameters
,例如:
public class Parent<T>
{
public string ParentName { get; set; }
[JsonProperty(ItemConverterType = typeof(InterfaceToConcreteGenericJsonConverter), ItemConverterParameters = new object[] { typeof(Example<>) })]
public List<IExample<T>> ExampleList { get; set; }
}