How to get the name of from generic type and pass it into JsonProperty()?

前端 未结 3 1866
盖世英雄少女心
盖世英雄少女心 2020-12-06 10:40

I get the following error with the code below:

\"An object reference is required for the non-static field, method, or property \'Response.PropName\'

3条回答
  •  春和景丽
    2020-12-06 11:40

    What you're trying to do is possible, but not trivial, and can't be done with only the built-in attributes from JSON.NET. You'll need a custom attribute, and a custom contract resolver.

    Here's the solution I came up with:

    Declare this custom attribute:

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

    Apply it to your Data property

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

    (0 is the position of T in Response's generic type parameters)

    Declare the following contract resolver, which will look for the JsonPropertyGenericTypeName attribute and get the actual name of the type argument:

    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;
        }
    }
    

    Serialize with this resolver in your serialization settings:

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

    This will give the following output for Response

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

提交回复
热议问题