Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
361 views
in Technique[技术] by (71.8m points)

c# - Json.NET getting generic property types name on serialization?

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...