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

时间:2016-08-24 16:31:51

标签: c# json.net

我使用以下代码收到以下错误:

  

&#34;非静态字段,方法或者需要对象引用   property&#39; Response.PropName&#39;&#34;

代码:

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

3 个答案:

答案 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是TResponse<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)

@Thomas Levesque:好的。所以,假设你不能在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"}