0

I'm trying to return objects based on query strings.
For example, I would like api/users/{id}?fields=username,email,reputation to return an object of type User that contains only the three included properties (username, email, reputation).

On a side note, comma separated query strings are not possible by default in .NET Core.
Here's a tutorial on making that work.

Following the guide above, I have a list of strings. How can I create an object that only includes the properties that match the names of those strings?
For only a few strings I can do this (thanks Ben Hall):

List<string> listOfStrings ...; // Strings from query
User user = GetUser(id); // User from db 

User newUser = new User();

if (listOfStrings.Contains("username"))
    newUser.username = user.username;
if (listOfStrings.Contains("email"))
    newUser.email = user.email;
if (listOfStrings.Contains("reputation"))
    newUser.reputation = user.reputation;

But for a long list of strings (my user class has 30+ properties) how would I go about doing this?

For reference, Facebook Graph API does this.

brad
  • 1,407
  • 19
  • 33
  • you _could_ use reflection on the public properties of `typeof(user)` and only copy those fields from `user` to `newUser` that are named but ... why? for asp.net, simply do not display anything from the `user` thats not in the query string... – Patrick Artner Nov 11 '17 at 19:24
  • You can just return an anonymous object that has those proprties. – CodingYoshi Nov 11 '17 at 19:30
  • I was making a mistake with the if statement placement lol – brad Nov 11 '17 at 19:32

3 Answers3

2
List<string> listOfStrings ...; // Strings from query
User user = GetUser(id); // User from db 

User newUser = new User();
//gettting object type
var userType = user.GetType();

This solution needs some knowledge about Reflections
It uses the methods GetValue and SetValue
(The code is tested in Visual Studio)
Now foreach element in listOfStrings

foreach(var propertyName in listOfString){
// This line of code retrives the value of the propety in the user class
 var retrivedValue = userType.GetProperty(propertyName).GetValue(user);
// This line of code sets the value retrived to the property in the newUser class
 userType.GetProperty(propertyName).SetValue(newUser, retrivedValue , null);
}
Ardit
  • 376
  • 4
  • 9
  • This works, thank you. Although my goal was to return the newUser object anonymously (only including the properties that are given in the query). Is this possible? – brad Nov 11 '17 at 22:32
  • Essentially i'd like to do exactly what the Facebook Graph API does. The returned JSON object should only contain the fields specified in the query. – brad Nov 11 '17 at 22:45
  • I achieved this by creating a `IDictionary`, checking for null inside the foreach and storing as `propertyName` key and `retreivedValue` value. I then return the dictionary which only includes the properties that were given and were not null. This doesn't use all of the code you wrote above but it was valuable to learn about nonetheless. Thank you again for your help :) – brad Nov 11 '17 at 23:02
  • One other way is using Json.net [NullValueHandling-Enumeration](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_NullValueHandling.htm) returning just non null values from object. – Ardit Nov 12 '17 at 09:04
  • 1
    Oh great! Thanks. AutoMapper also has null value handling :) – brad Nov 12 '17 at 23:58
1

We could explore other ways like reflection but if should still serve you well enough. Looks like you're making a simple mistake trying to use an If in an object initialiser. You will have to do that after instaniating the new User object e.g.

User newUser = new User();
if (listOfStrings.Contains("username"))
{
       newUser.username = user.username;
}
Ben Hall
  • 1,353
  • 10
  • 19
  • If my potential list of strings is long, how would this be best approached? – brad Nov 11 '17 at 19:41
  • 2
    Getting into reflection territory. I've talked about [similar things](https://stackoverflow.com/questions/40041905/web-api-handle-misspelled-fromuri-parameters/40043835#40043835) but not exactly this problem and can't say I have ever used a [string name to access a property](https://stackoverflow.com/questions/2905187/accessing-object-property-as-string-and-setting-its-value). – Ben Hall Nov 11 '17 at 20:25
0

Turns out there is actually Middleware for this called Popcorn.

Popcorn is a communication protocol on top of a RESTful API that allows requesting clients to identify individual fields of resources to include when retrieving the resource or resource collection.

It allows for a recursive selection of fields, allowing multiple calls to be condensed into one.

Features:

  • Selective inclusion from a RESTful API
  • Configurable response defaults
  • Sorting of responses
  • Selective authorization and permissioning of response values
  • Configurable response inspectors
  • Factory and advanced projection support
  • Set relevant contexts for your API
  • Blind expansion f response objects
brad
  • 1,407
  • 19
  • 33