2016-07-07 11 views
5

私は、親クラスと同じタイプのジェネリックインターフェイスであるパブリックプロパティを含むジェネリッククラスを持っています。以下のコード例。ジェネリックインターフェイスプロパティに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; } 
} 

私は多くのParent<T>オブジェクトを含めることができますMainClassオブジェクトをシリアル化するためにJSON.netを使用しています。 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()メソッドを呼び出す前に、シリアライザにそれぞれの予想されるタイプTに新しいInterfaceJsonConverterを手動で追加することです。ただし、デシリアライズできる場合は、各タイプを手動で追加する必要があるため、可能なタイプはParent<T>です。

一般的な方法でこれを逆シリアル化する方法はありますか?私が取るべき別のアプローチがありますか?

答えて

3

これは適切な構築ReadJson()内部次いで、コンストラクタ引数として適切なJsonConverteropen generic typetypeof(Example<>)を通過させることによって、間接的にもかかわらず、行うことができるが、所望のコンクリートと同様、一般的なパラメータがあり、渡された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(); 
    } 
} 

は、それを適用します。

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