将JSON反序列化为通用接口属性

jef*_*237 5 .net c# generics serialization json

我有一个泛型类,它包含一个公共属性,它是与父类相同类型的通用接口.示例代码如下.

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; }
}
Run Code Online (Sandbox Code Playgroud)

我正在使用JSON.net来序列化MainClass可以包含许多Parent<T>对象的对象. Parent<T>可以是没有类型约束的任何泛型.但是,我似乎无法以通用方式反序化生成的JSON.

我试图JsonConverter为JSON.net反序列化器创建一个,但我找不到一种通用的方法.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);
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的转换器允许将属性放在类的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; }
}
Run Code Online (Sandbox Code Playgroud)

但是c#不允许在属性中使用泛型类型引用(因为属性和反射的性质).到目前为止,我提出的唯一解决方案是InterfaceJsonConverter在调用Deserialize()方法之前手动将每个预期类型T 的新增加到序列化程序.但是,这限制了可能的类型,Parent<T>因为如果可以反序列化,则需要手动添加每种类型.

有没有办法以通用方式反序列化?我应该采取另一种方法吗?

dbc*_*dbc 5

可以通过间接方式将开放通用类型 传递typeof(Example<>)给适当JsonConverter的构造函数参数,然后ReadJson()通过假定objectType传入的参数与所需的具体封闭通用类型相同的通用参数来内部构造合适的封闭通用类型,从而间接地做到这一点。

还要注意,只要使用将该转换器直接应用于属性[JsonConverter(Type,Object[])],转换器就不必知道接口类型,因为它CanConvert()不会被调用。 CanConvert()仅当转换器在JsonSerializer.Converters列表中时才调用。

因此,您的转换器变为:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

如下应用:

public class Parent<T>
{
    public string ParentName { get; set; }

    [JsonConverter(typeof(InterfaceToConcreteGenericJsonConverter), new object[] { typeof(Example<>) })]
    public IExample<T> ExampleItem { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

要将此类带有参数的转换器应用于收集项目,请使用JsonPropertyAttribute.ItemConverterTypeJsonPropertyAttribute.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; }
}
Run Code Online (Sandbox Code Playgroud)