2016-08-24 2 views
10

は、私は以下のコードで、次のエラーが表示されます。generic型から<T>の名前を取得してJsonProperty()に渡すにはどうすればいいですか?</p> <blockquote> <p>「オブジェクト参照が非静的フィールド、メソッドのために必要とされる、または プロパティ 『Response.PropName』」</p> </blockquote> <p>コード:

+1

を属性には一定の値が必要です。 –

+1

@ DanielA.Whiteより正確には、**一定の**値。 –

+1

これは機能しません。属性は一定の値を必要とし、あなたの 'PropName'プロパティは実行時にのみ評価されます –

答えて

5

あなたがしようとしていることは可能ですが、些細なことではなく、JSON.NETの組み込み属性のみでは実行できません。カスタム属性とカスタム契約リゾルバが必要です。

ここで私が思いついた解決策です:

は、このカスタム属性を宣言します。

[AttributeUsage(AttributeTargets.Property)] 
class JsonPropertyGenericTypeNameAttribute : Attribute 
{ 
    public int TypeParameterPosition { get; } 

    public JsonPropertyGenericTypeNameAttribute(int position) 
    { 
     TypeParameterPosition = position; 
    } 
} 

public class Response<T> : Response 
{ 
    [JsonPropertyGenericTypeName(0)] 
    public T Data { get; set; } 
} 

(0 Response<T>Tの位置であり、あなたのDataプロパティに適用しますジェネリック型パラメータ)

JsonPropertyGenericTypeName属性を探します、次の契約リゾルバを宣言し、型引数の実際の名前を取得:

var settings = new JsonSerializerSettings { ContractResolver = new GenericTypeNameContractResolver() }; 
string json = JsonConvert.SerializeObject(response, settings); 

これは与える:あなたのシリアル化の設定で

class GenericTypeNameContractResolver : DefaultContractResolver 
{ 
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 
    { 
     var prop = base.CreateProperty(member, memberSerialization); 
     var attr = member.GetCustomAttribute<JsonPropertyGenericTypeNameAttribute>(); 
     if (attr != null) 
     { 
      var type = member.DeclaringType; 
      if (!type.IsGenericType) 
       throw new InvalidOperationException($"{type} is not a generic type"); 
      if (type.IsGenericTypeDefinition) 
       throw new InvalidOperationException($"{type} is a generic type definition, it must be a constructed generic type"); 
      var typeArgs = type.GetGenericArguments(); 
      if (attr.TypeParameterPosition >= typeArgs.Length) 
       throw new ArgumentException($"Can't get type argument at position {attr.TypeParameterPosition}; {type} has only {typeArgs.Length} type arguments"); 
      prop.PropertyName = typeArgs[attr.TypeParameterPosition].Name; 
     } 
     return prop; 
    } 
} 

シリアル化このリゾルバとしResponse<Foo>の出力

{ 
    "Foo": { 
    "Id": 0, 
    "Name": null 
    } 
} 
3

ここでは、それを実現するための簡単な方法があります。あなたがする必要があるのは、このように、応答はjオブジェクトを拡張することです。

public class Response<T>: Newtonsoft.Json.Linq.JObject 
{ 
    private static string TypeName = (typeof(T)).Name; 

    private T _data; 

    public T Data { 
     get { return _data; } 
     set { 
      _data = value; 
      this[TypeName] = Newtonsoft.Json.Linq.JToken.FromObject(_data); 
     } 
    } 
} 

あなたが行う場合は、その期待どおりに動作します以下:

static void Main(string[] args) 
    { 
     var p1 = new Response<Int32>(); 
     p1.Data = 5; 
     var p2 = new Response<string>(); 
     p2.Data = "Message"; 


     Console.Out.WriteLine("First: " + JsonConvert.SerializeObject(p1)); 
     Console.Out.WriteLine("Second: " + JsonConvert.SerializeObject(p2)); 
    } 

出力:

First: {"Int32":5} 
Second: {"String":"Message"} 

Response<T>がJObjectを拡張することができない場合は、Responseを拡張するために本当に必要なので、Response自体にJObjectを拡張させてから、Response<T>にResponseをb efore。まったく同じように動作するはずです。

+0

これは創造的な解決策ですが、「レスポンスをJObjectに拡張するだけです」というメッセージは非常に大きなものです。 –

+0

レスポンスを拡張できない場合は、コンポジションを使用して同様のコンテナクラスを作成できます応答のプロパティになります。私は後で例を追加しようとします... –

0

@Thomas Levesque:OK。したがって、既存のResponseクラスを拡張する必要があるため、Response<T>でJObjectを拡張できないとしましょう。ここでは、同じソリューションを実装することができ、別の方法があります:

public class Payload<T> : Newtonsoft.Json.Linq.JObject { 
    private static string TypeName = (typeof(T)).Name; 
    private T _data; 

    public T Data { 
     get { return _data; } 
     set { 
      _data = value; 
      this[TypeName] = Newtonsoft.Json.Linq.JToken.FromObject(_data); 
     } 
    } 
} 

//Response is a pre-existing class... 
public class Response<T>: Response { 
    private Payload<T> Value; 

    public Response(T arg) { 
     Value = new Payload<T>() { Data = arg };    
    } 

    public static implicit operator JObject(Response<T> arg) { 
     return arg.Value; 
    } 

    public string Serialize() { 
     return Value.ToString(); 
    } 
} 

だから今、クラスをシリアル化するために、次のオプションがあります。

static void Main(string[] args) { 
     var p1 = new Response<Int32>(5); 
     var p2 = new Response<string>("Message"); 
     JObject p3 = new Response<double>(0.0); 
     var p4 = (JObject) new Response<DateTime>(DateTime.Now); 

     Console.Out.WriteLine(p1.Serialize()); 
     Console.Out.WriteLine(p2.Serialize()); 
     Console.Out.WriteLine(JsonConvert.SerializeObject(p3)); 
     Console.Out.WriteLine(JsonConvert.SerializeObject(p4)); 
    } 

出力は次のようになります:

{"Int32":5} 
{"String":"Message"} 
{"Double":0.0} 
{"DateTime":"2016-08-25T00:18:31.4882199-04:00"} 
関連する問題

 関連する問題