如何从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"}