2016-07-07 15 views
5

Tôi có một lớp chung chứa một thuộc tính công khai là giao diện chung của cùng loại với lớp cha. Mã ví dụ bên dưới.Deserializing JSON thành thuộc tính giao diện chung

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

Tôi đang sử dụng JSON.net để tuần tự hóa đối tượng MainClass có thể chứa nhiều đối tượng Parent<T>. Parent<T> có thể là bất kỳ chung chung không có ràng buộc loại. Tuy nhiên, tôi không thể dường như deserialize JSON kết quả trong một thời trang chung chung.

Tôi đã cố gắng tạo một JsonConverter cho trình gỡ rối JSON.net, nhưng tôi không thể tìm cách áp dụng nó một cách tổng quát. Ví dụ JsonConverter bên dưới.

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

Bộ chuyển đổi trên sẽ cho phép một thuộc tính được đặt trên ExampleItem tài sản của Parent<T> lớp như:

public class Parent<T> 
{ 
    public string ParentName { get; set; } 
    [JsonConverter(typeof(InterfaceJsonConverter<IExample<T>, Example<T>>))] 
    public IExample<T> ExampleItem { get; set; } 
} 

Nhưng C# không cho phép bạn có tài liệu tham khảo kiểu chung chung trong các thuộc tính (vì bản chất của các thuộc tính và sự phản chiếu). Giải pháp duy nhất tôi đã đưa ra cho đến nay là thêm InterfaceJsonConverter mới cho mỗi kiểu T dự kiến ​​theo cách thủ công vào bộ nối tiếp trước khi gọi phương thức Deserialize(). Tuy nhiên, điều này giới hạn các loại có thể là Parent<T> vì mỗi loại cần phải được thêm thủ công nếu có thể được loại bỏ.

Có cách nào để deserialize điều này một cách chung chung? Có cách tiếp cận khác mà tôi nên dùng không?

Trả lời

3

Điều này có thể được thực hiện, mặc dù gián tiếp, bằng cách thông qua các open generic typetypeof(Example<>) đến một thích hợp JsonConverter như một đối số nhà xây dựng, sau đó bên ReadJson() xây dựng một thích hợp đóng kiểu chung chung bằng cách giả sử các objectType thông qua trong có các thông số chung chung giống như bê tông mong muốn loại chung đóng. Lưu ý rằng, miễn là bộ chuyển đổi được áp dụng trực tiếp cho một tài sản sử dụng [JsonConverter(Type,Object[])], nó không cần thiết cho bộ chuyển đổi để biết loại giao diện, vì CanConvert() sẽ không được gọi. CanConvert() chỉ được gọi khi bộ chuyển đổi nằm trong danh sách JsonSerializer.Converters.

Như vậy chuyển đổi của bạn trở thành:

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

Áp dụng nó như sau:

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

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

Để áp dụng một chuyển đổi như vậy với các thông số để các khoản thu, sử dụng JsonPropertyAttribute.ItemConverterTypeJsonPropertyAttribute.ItemConverterParameters, ví dụ:

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

Điều này đã hiệu quả! Không nghĩ về việc sử dụng sự phản chiếu bên trong bộ chuyển đổi - giải pháp tuyệt vời. Cảm ơn bạn! – jeff17237

Các vấn đề liên quan