你想要做的事情是可能的,但不是一件简单的事情,并且不能仅使用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代表在Response<T>
的泛型类型参数中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
}
}
PropName
属性只在运行时评估。 - Thomas Levesque