Json.NET在序列化上获得通用属性类型名称?

时间:2014-10-29 11:41:43

标签: c# .net json serialization json.net

我正在试图弄清楚如何从我的API返回核心对象

public class Response<T> {
    public T Data {get;set;}
}

其中T是具有属性的某个对象,例如

public class Thang {
   public string Thing  {get;set;}
}

使用JsonConvert.Serialize( myResponse );会将T Data属性返回为Data,这是正确的。

但是如果我想使用T类型的名称呢?因此,响应Json实际上会包含一个名为Thang而不是Data的属性,如下所示。

{
    "thang": {
        "thing" : "hey"
    }
}

我很好奇是否有一种相对简单的方法可以使用Json.net执行此操作,或者您是否必须创建自定义JsonConverter并使用反射来获取T类型名称写?

感谢。

1 个答案:

答案 0 :(得分:2)

没有内置的方法可以做到这一点,我知道。

您确实需要使用一点反射,并且您可以使用自定义JsonConverter,但您也可以使用自定义ContractResolver在几行代码中执行此操作:

public class GenericPropertyContractResolver :
      CamelCasePropertyNamesContractResolver
{
    private readonly Type genericTypeDefinition;

    public GenericPropertyContractResolver(Type genericTypeDefinition)
    {
        this.genericTypeDefinition = genericTypeDefinition;
    }

    protected override JsonProperty CreateProperty(
        MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty baseProperty =
            base.CreateProperty(member, memberSerialization);

        Type declaringType = member.DeclaringType;

        if (!declaringType.IsGenericType ||
            declaringType.GetGenericTypeDefinition() != this.genericTypeDefinition)
        {
            return baseProperty;
        }

        Type declaringGenericType = declaringType.GetGenericArguments()[0];

        if (IsGenericMember(member))
        {
            baseProperty.PropertyName =
                this.ResolvePropertyName(declaringGenericType.Name);
        }

        return baseProperty;
    }

    // Is there a better way to do this? Determines if the member passed in
    // is a generic member in the open generic type.
    public bool IsGenericMember(MemberInfo member)
    {
        MemberInfo genericMember = 
            this.genericTypeDefinition.GetMember(member.Name)[0];

        if (genericMember != null)
        {
            if (genericMember.MemberType == MemberTypes.Field)
            {
                return ((FieldInfo)genericMember).FieldType.IsGenericParameter;
            }
            else if (genericMember.MemberType == MemberTypes.Property)
            {
                PropertyInfo property = (PropertyInfo)genericMember;

                return property
                    .GetMethod
                    .ReturnParameter
                    .ParameterType
                    .IsGenericParameter;
            }
        }

        return false;
    }
}

然后您可以像这样使用它:

var settings = new JsonSerializerSettings();
settings.ContractResolver = new GenericPropertyContractResolver(typeof(Response<>));

string serialized = JsonConvert.SerializeObject(new Response<Thang> 
{ 
    Data = new Thang { Thing = "Hey" }
}, settings);

可能更直接的做法是在序列化之前将您的班级变成Dictionary

我在确定封闭泛型类型上的属性是否与开放泛型类型的通用属性相对应时也遇到了一些问题 - 任何提示都会受到赞赏。

示例: https://dotnetfiddle.net/DejOL2