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

5

我有一个通用类,其中包含一个公共属性,该属性是与父类相同类型的通用接口。以下是示例代码。

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);
    }
}

上述转换器将允许在Parent<T>类的ExampleItem属性上放置一个属性,例如:
public class Parent<T>
{
    public string ParentName { get; set; }
    [JsonConverter(typeof(InterfaceJsonConverter<IExample<T>, Example<T>>))]
    public IExample<T> ExampleItem { get; set; }
}

但是C#不允许在属性中使用泛型类型引用(因为属性和反射的性质)。到目前为止,我想到的唯一解决方案是在调用Deserialize()方法之前手动向序列化程序添加一个新的InterfaceJsonConverter,以便为每个预期类型T添加。然而,这限制了可能的Parent<T>类型,因为如果可以反序列化,则需要手动添加每个类型。有没有办法以通用的方式反序列化它?我应该采取另一种方法吗?
1个回答

5
可以通过向适当的 JsonConverter 传递 开放泛型类型typeof(Example<>) 作为构造函数参数间接地实现,然后在 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.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; }
}

1
这个可行!没有想到在转换器内部使用反射 - 很棒的解决方案。谢谢! - jeff17237

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接