Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get JSON.NET in WebAPI to serialize exact type not subclass

I have three classes in a domain

public class ArtistInfo
{
    private Guid Id { get; set; }
    public string Name { get; set; }
    public string DisplayName { get; set; }
    public bool IsGroup { get; set; }
    public bool IsActive { get; set; }
    public string Country { get; set; }
}

public class Artist : ArtistInfo
{
    public DateTime Created { get; set; }
    public int CreatedById { get; set; }
    public DateTime Updated { get; set; }
    public int UpdatedById { get; set; }
}

public class Track
{
    public string Title { get; set; }
    public string DisplayTitle { get; set; }
    public int Year { get; set; }
    public int Duration { get; set; }
    public int? TrackNumber { get; set; }
    //[SomeJsonAttribute]
    public ArtistInfo Artist { get; set; }
}

From the ASP.NET Web API I return a generic List(of Tracks). No matter what I have tried the Web API returns the Artist property of Track as an Artist not ArtistInfo. Is there someway to limit this in the output of the API to just use ArtistInfo? I don't want to write addition "ViewModels/DTOs" to handle this situation. Can I decorate the ArtistInfo with a hint to the JSON Serializer?

like image 587
BuddyJoe Avatar asked Nov 10 '22 03:11

BuddyJoe


1 Answers

One way to get the result you want is to use a custom JsonConverter that can limit the set of properties serialized. Here is one that will only serialize the properties of a base type T:

public class BaseTypeConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JObject obj = new JObject();
        foreach (PropertyInfo prop in typeof(T).GetProperties())
        {
            if (prop.CanRead)
            {
                obj.Add(prop.Name, JToken.FromObject(prop.GetValue(value)));
            }
        }
        obj.WriteTo(writer);
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

To use the converter, mark the Artist property in your Track class with a [JsonConverter] attribute as shown below. Then, only the ArtistInfo properties of the Artist will be serialized.

public class Track
{
    public string Title { get; set; }
    public string DisplayTitle { get; set; }
    public int Year { get; set; }
    public int Duration { get; set; }
    public int? TrackNumber { get; set; }
    [JsonConverter(typeof(BaseTypeConverter<ArtistInfo>))]
    public ArtistInfo Artist { get; set; }
}

Demo here

like image 129
Brian Rogers Avatar answered Nov 14 '22 22:11

Brian Rogers