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



我得到以下代码的错误:

"非静态字段、方法或房地产的反应。PropName"

代码:

public class Response<T> : 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<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
  }
}

这里有一个可能更容易实现的方法。您所需要做的就是让Response扩展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。应该是一样的。

@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"}

相关内容

  • 没有找到相关文章

最新更新