-1

I have a .NET 7 web api with a route that returns Users.

public class User
{
    public Guid? Id { get; set; }
    public String? Name { get; set; }
    public Int32? Age { get; set; }
    public String? HairColor { get; set; }
}

If "Name" comes in the Select query param, the database only returns the Name property and the Ok(users); returns. Ex:

[
    {
        "id": null,
        "name": "Test1",
        "age": null,
        "hairColor": null
    },
    {
        "id": null,
        "name": "Test2",
        "age": null,
        "hairColor": null
    },
    {
        "id": null,
        "name": "Test3",
        "age": null,
        "hairColor": null,
    }
]

To save on package size, I would instead like it to return:

[
    {
        "name": "Test1"
    },
    {
        "name": "Test2"
    },
    {
        "name": "Test3"
    }
]

But, if nothing comes in the Select query param, all of the properties get populated and returned even if null/default. How can I dynamically set

JsonSerializerOptions options = new()
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
};

So that if Select is set, Ok(users) ignores the null/default properties when serializing, otherwise it returns all properties?

ScubaSteve
  • 7,724
  • 8
  • 52
  • 65
  • 1
    Can’t you just return an anonymous object that just projects the `Name` property? – stuartd Feb 08 '23 at 01:51
  • A newtonsoft contract resolver can filter / ignore properties, which you could customise per request. I don't think there is a system.text equivalent. The closest would be a custom converter, but you'll have to implement the low level json reading / writing. – Jeremy Lakeman Feb 08 '23 at 02:17
  • 1
    @JeremyLakeman - In .NET 7 System.Text.Json made its contract resolver public, see [System.Text.Json API is there something like IContractResolver](https://stackoverflow.com/a/74284215/3744182). Main issue looks to be how to use different options for specific requests as the question states. – dbc Feb 08 '23 at 02:22

2 Answers2

1

IMHO the simpliest way is

    if (users[0].Id != null) return Ok(users); //or more validations
    return Ok( users.Select(u => new { Name = u.Name }) );
        
Serge
  • 40,935
  • 4
  • 18
  • 45
  • Utilizing an anonymous type is interesting. I would need to make the creation of it dynamic as I don't know what property names are being sent in. I suppose I could check if `Select` has value, and if so, loop through the values and use reflection to pull those properties from the model and put them into this anonymous object. – ScubaSteve Feb 08 '23 at 16:28
  • @ScubaSteve "as I don't know what property names are being sent in."? What do you mean? In your question you wrote you had a list of users. – Serge Feb 08 '23 at 16:39
  • The database is returning a list of users, yes. But, what property names are in `Select` is dynamic. So, the requestor may leave it null, meaning all properties of User are returned for each User. Or, for example, it could have Age and HairColor, in which only those properties would be returned for each User. – ScubaSteve Feb 08 '23 at 16:52
0

I'm not sure if this is the best way, but utilizing anonymous types was a spark. I thought, I would need to dynamically and recursively find all of the properties in Select, then add it to the anonymous type. Instead of reinventing the wheel, why not just utilize System.Text.Json?

if (select is not null && select.Count > 0)
{
    var modelsWithLessProperties = JsonSerializer.Deserialize<object>(JsonSerializer.Serialize(models, new JsonSerializerOptions
    {
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
    }));

    return Ok(modelsWithLessProperties);
}

return Ok(models);

Serializing, then deserializing may not be the most efficient way of doing this, but it is utilizing established code.

I am open to any, more efficient, established (i.e. not error prone) way of doing this.

ScubaSteve
  • 7,724
  • 8
  • 52
  • 65