Only return selected fields in Web API results

Don't serialize your DAO. Create a complete contract and then serialize it selectively. To creating different contracts for different cases, you could simplify it using Json.Net; you could just create a custom contract resolver and use it as a parameter of SerializeObject() like so

static void Main(string[] args)
{
    var person = new TestContract {FirstName = "John", LastName = "Doe", Age = 36};

    var firstNameContract = new SelectiveSerializer("firstname");
    var allPropertiesContract = new SelectiveSerializer("firstname, lastname, age");

    var allJson = JsonConvert.SerializeObject(
        person, 
        Formatting.Indented,
        new JsonSerializerSettings {ContractResolver = allPropertiesContract});

    var firstNameJson = JsonConvert.SerializeObject(
        person, 
        Formatting.Indented,
        new JsonSerializerSettings {ContractResolver = firstNameContract});

    Console.WriteLine(allJson);
    //  {
    //    "FirstName": "John",
    //    "LastName": "Doe",
    //    "Age": 36
    //  }


    Console.WriteLine(firstNameJson);
    //  {
    //    "FirstName": "John",    
    //  }        
}

public class SelectiveSerializer : DefaultContractResolver
{
    private readonly string[] _fields;

    public SelectiveSerializer(string fields)
    {
        var fieldColl = fields.Split(',');
        _fields = fieldColl
            .Select(f => f.ToLower().Trim())
            .ToArray();
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        property.ShouldSerialize = o => _fields.Contains(member.Name.ToLower());

        return property;
    }
}

public class TestContract
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public int Age { get; set; }
}

Without much effort, you could probably work this into your default mediatype formatter (in the pipeline) to look for a parameter in the request called 'fields' or whatever and then use the custom contract resolver if present, and then it would be seamless default behavior to limit fields if specified or serialize the entire object if not specified.

On the academic side, here is the justification: Any modification to the data is considered a "view concern" which means, in an API, it should controlled by query parameters and accept header. In this case, the "representation" of the data is application/json and you've chose to "filter" the returned fields. All of this can (and should be, imo) be handled during serialization. So your "model" in this case will always be the full model vs. some subset of the model. The full model in this example contains first name, last name, and age. In reality, this could be hundreds of properties. If you want to allow the client to choose a subset of the complete model, this is how you could do it with selective serialization.

You can similar behaviors in graph apis. There, the default for large models is that you get an empty object if you don't specify fields, forcing the client to be very specific about what it asks for, which is great when payload size matters (e.g. mobile applications). And, there's nothing stopping from creating field presets like 'name' which could mean 'firstname, lastname' or 'all' which includes all properties.

I've never been a fan of having hundreds of data objects that all serve some ad hoc requirement for a data set that is used in 20 different contexts where some cases require more data while others require less. IMO if you have to go through the same process to get the data, whether it complete or not, you shouldn't waste your time creating additional objects to frame the data for the sake of the client, and this should help you achieve that.


It's because you're returning a collection of Publication objects so you will get every property that is contained in that class, whether you populate it or not. If you want to return a subset of the properties then create a class that has only the properties you want to return and create an instance of that class in your query.

public IQueryable<WhatIReallyWantToReturn> GetPublications()
{
    return db.Publications
        .ToList()
        .Select(p => new WhatIReallyWantToReturn {
            PublicationID = p.PublicationID,
            PublicationTitle = p.PublicationTitle,
            Frequency = p.Frequency,
            NextIssueDate = p.NextIssueDate
        })
        .AsQueryable();
}

private class WhatIReallyWantToReturn
{
    public int PublicationID { get; set; }
    public string PublicationTitle { get; set; }
    public string Frequency { get; set; }
    public DateTime NextIssueDate { get; set; }
}

using Newtonsoft.Json;

public class Publication
{
    [Key]
    public int PublicationID { get; set; }
    public string PublicationTitle { get; set; }
    public string Frequency { get; set; }
    public DateTime NextIssueDate { get; set; }
    public DateTime SpaceDeadline { get; set; }
    public DateTime MaterialsDeadline { get; set; }

    [JsonIgnore]    
    public DateTime CreatedDt { get; set; }

    [JsonIgnore]    
    public string CreatedBy { get; set; }

    [JsonIgnore]    
    public DateTime UpdatedDt { get; set; }

    [JsonIgnore]    
    public string UpdatedBy { get; set; }
}