Reputation: 2905
I'm trying to figure out how I can have a core object being returned from my API
public class Response<T> {
public T Data {get;set;}
}
Where T is some object with properties e.g.
public class Thang {
public string Thing {get;set;}
}
Using JsonConvert.Serialize( myResponse );
will return the T Data
property as Data
, and rightly so.
But what If I wanted to use the name for the type of T
? So the response Json would actually include a property called Thang
not Data
as follows.
{
"thang": {
"thing" : "hey"
}
}
I'm curious if there is a relatively simple way to do this with Json.net or do you have to create a custom JsonConverter
and use reflection to get the T
type name when writing?
Thanks.
Upvotes: 4
Views: 3067
Reputation: 126042
There's no built-in way to do this that I'm aware of.
You do need to use a little reflection, and you could probably use a custom JsonConverter
, but you could also do it in just a few lines of code using a custom 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;
}
}
You could then use it like this:
var settings = new JsonSerializerSettings();
settings.ContractResolver = new GenericPropertyContractResolver(typeof(Response<>));
string serialized = JsonConvert.SerializeObject(new Response<Thang>
{
Data = new Thang { Thing = "Hey" }
}, settings);
Possibly a more straightforward thing to do would be to turn your class into a Dictionary
before serializing it.
I also had a little trouble determining if a property on a closed generic type corresponded to a generic property on the open generic type--any tips on that would be appreciated.
Example: https://dotnetfiddle.net/DejOL2
Upvotes: 2