2016-08-24 24 views
10

aşağıda kod ile aşağıdaki hatayı alıyorum:Genel türden <T> adı nasıl alınır ve JsonProperty()?</p> <blockquote> <p>"Bir nesne başvurusu statik olmayan alan, yöntem için gerekli olan veya mülkiyet 'Response.PropName'"</p> </blockquote> <p>Kodu:

public class Response<T> : Response 
{ 
    private string PropName 
    { 
     get 
     { 
      return typeof(T).Name; 
     } 
    }    
    [JsonProperty(PropName)] 
    public T Data { get; set; } 
} 
+1

öznitelikler sabit değerler gerektirir. –

+1

@ DanielA.White daha kesin, ** sabit ** değerleri. –

+1

Bu işe yaramaz; öznitelikler sabit değerlere ihtiyaç duyar ve 'PropName' özelliğiniz yalnızca çalışma zamanında değerlendirilir. –

cevap

5

Yapmaya çalıştığınız şey mümkün, ancak önemsiz değil ve yalnızca JSON.NET'in yerleşik öznitelikleriyle yapılamaz. Özel bir özelliğe ve özel bir sözleşme çözümleyicisine ihtiyacınız olacak.

Declare bu özel özellik:

[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> içinde T konumudur sizin Data özelliğine uygulayın

İşte çözüm ben ile geldi bulunuyor Genel tür parametreleri

JsonPropertyGenericTypeName öznitelik için bakacağız aşağıdaki sözleşme çözümleyicinizi beyan ve tip argüman gerçek adını almak:

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

Bu verecektir: En seri ayarlarında

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

serialize bu resolverli Response<Foo>

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

için aşağıdaki çıkış İşte bunu başarmak için potansiyel daha kolay bir yoldur. Tek yapmanız gereken tek şey bu gibi Tepki JObject uzatmak sahip olmaktır:

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

Bunu yaparsanız, o beklediğiniz gibi çalışacak aşağıdaki:

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

Çıktı:

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

Response<T>, JObject 'i genişletemezseniz, Yanıtı genişletmek için gerçekten ihtiyacınız olduğu için, Yanıtın kendisi JObject' i genişletebilir ve Response<T> Yanıtı b olarak uzatır nceki. Sadece aynı şekilde çalışmalı.

+0

Bu yaratıcı bir çözümdür, ancak "Yapmanız gereken tek şey, Response'i JObject'i uzatmaktır" oldukça büyük bir gereksinimdir ... –

+0

Eğer Yanıt uzatılamazsa, kompozisyonu kullanabilir ve benzer bir Kapsayıcı sınıfı yaratabilir Bu, Response 'un bir özelliği olacaktır. Daha sonra bir örnek eklemeyi deneyeceğim ... –

0

@Thomas Levesque: Tamam. Öyleyse, JObject’i Response<T>’a genişletemeyeceğinizi varsayalım, çünkü önceden var olan bir Response sınıfını genişletmelisiniz.Burada aynı çözümü uygulamak olabilir başka bir yol:

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

Yani şimdi sınıfını seri hale getirilmeye aşağıdaki seçenek vardır:

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

Çıktı bu gibi bir şey olacaktır:

{"Int32":5} 
{"String":"Message"} 
{"Double":0.0} 
{"DateTime":"2016-08-25T00:18:31.4882199-04:00"}