我使用以下代码收到以下错误:
"非静态字段,方法或者需要对象引用 property' Response.PropName'"
代码:
public class Response<T> : Response
{
private string PropName
{
get
{
return typeof(T).Name;
}
}
[JsonProperty(PropName)]
public T Data { get; set; }
}
答案 0 :(得分:8)
您尝试做的事情是可能的,但不是微不足道的,只能使用JSON.NET中的内置属性。您需要自定义属性和自定义合约解析程序。
以下是我提出的解决方案:
声明此自定义属性:
[AttributeUsage(AttributeTargets.Property)]
class JsonPropertyGenericTypeNameAttribute : Attribute
{
public int TypeParameterPosition { get; }
public JsonPropertyGenericTypeNameAttribute(int position)
{
TypeParameterPosition = position;
}
}
将其应用于Data
媒体资源
public class Response<T> : Response
{
[JsonPropertyGenericTypeName(0)]
public T Data { get; set; }
}
(0是T
在Response<T>
的泛型类型参数中的位置)
声明以下合约解析器,它将查找JsonPropertyGenericTypeName
属性并获取类型参数的实际名称:
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;
}
}
在序列化设置中使用此解析程序进行序列化:
var settings = new JsonSerializerSettings { ContractResolver = new GenericTypeNameContractResolver() };
string json = JsonConvert.SerializeObject(response, settings);
这将为Response<Foo>
{
"Foo": {
"Id": 0,
"Name": null
}
}
答案 1 :(得分:3)
这是一种可能更容易实现的方法。您需要做的就是让Response extend JObject,如下所示:
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。它应该工作相同。
答案 2 :(得分:0)
Response<T>
中扩展JObject,因为你需要扩展一个预先存在的Response类。这是您可以实现相同解决方案的另一种方式:
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"}