如何从generics类型获取的名称并将其传递给JsonProperty()?

我使用下面的代码得到以下错误:

“非静态字段,方法或属性’Response.PropName’需要对象引用”

码:

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

你想要做的事情是可能的,但不是微不足道的,只能用JSON.NET的内置属性来完成。 您需要自定义属性和自定义合约解析程序。

这是我提出的解决方案:

声明此自定义属性:

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

将其应用于您的Data属性

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

(0是Response的generics类型参数中T的位置)

声明以下合约解析器,它将查找JsonPropertyGenericTypeName属性并获取type参数的实际名称:

 class GenericTypeNameContractResolver : DefaultContractResolver { protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var prop = base.CreateProperty(member, memberSerialization); var attr = member.GetCustomAttribute(); 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; } } 

在序列化设置中使用此解析程序进行序列化:

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

这将为Response提供以下输出

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

这是一种可能更容易实现的方法。 您需要做的就是让Response extend JObject,如下所示:

 public class Response: 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(); p1.Data = 5; var p2 = new Response(); p2.Data = "Message"; Console.Out.WriteLine("First: " + JsonConvert.SerializeObject(p1)); Console.Out.WriteLine("Second: " + JsonConvert.SerializeObject(p2)); } 

输出:

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

如果您不能使用Response扩展JObject,因为您确实需要它来扩展Response,您可以让Response本身扩展JObject,然后像以前一样使Response扩展Response。 它应该工作相同。

@Thomas Levesque:好的。 因此,假设您无法在Response扩展JObject,因为您需要扩展预先存在的Response类。 这是您可以实现相同解决方案的另一种方式:

 public class Payload : 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: Response { private Payload Value; public Response(T arg) { Value = new Payload() { Data = arg }; } public static implicit operator JObject(Response arg) { return arg.Value; } public string Serialize() { return Value.ToString(); } } 

所以现在有以下选项来序列化类:

  static void Main(string[] args) { var p1 = new Response(5); var p2 = new Response("Message"); JObject p3 = new Response(0.0); var p4 = (JObject) new Response(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"}