0

I have a controller mapped to API endpoint which takes a list of objects (say Dog) and then I deserialize it into IEnumerable.How do I know if this object is of type Dog or Cat or whatever ? My controller looks like this

string objectType  = "Dog"  //can be anything like Dog,Cat etc

var list = System.Text.Json.JsonSerializer
    .Deserialize<IEnumerable<objectType>>(content.ToString());

var output = services.InsertObject<objectType>(list);

Obviously I cannot write IEnumerable<objectType> since it throws error. Since I want to pass on this dynamic Object type to other layers as well, I want it to be generic. My methods look like this. InsertObject<T>(IEnumerable<T>items);

netcoredev
  • 35
  • 2
  • 7
  • 1
    Even if you manage to get your `list` object to contain the correct type of objects, how would you expect to use that item without knowing its contents? – DavidG Oct 08 '21 at 11:15
  • How is the code in your question *called*? – Lasse V. Karlsen Oct 08 '21 at 11:24
  • @DavidG , didnt get you. I want to convert objectType string variable which contains the type "Dog" and use that in IEnumerable (T means Dog here).I cant pass IEnumerable .Obviously one way is I can check if objectType is Dog,then do IEnumerable,but there are hell lot of objects.Cant do it this way. – netcoredev Oct 08 '21 at 11:25
  • @LasseV.Karlsen , Insert(string objectType,[FromBody] content){..} – netcoredev Oct 08 '21 at 11:27

1 Answers1

2

By default, you can't have one API endpoint that accepts different object types, because the serializer can't guess which object type to deserialize into.

You need some discriminator to tell the serializer what kind of object to deserialize from the body.

Given this body:

{
    "name": "Foo",
    "size": "Large"
}

Which of these classes does that match?

public class TShirt
{
    public string Name { get; set; }
    public string Size { get; set; }
}

public class Dog
{
    public string Name { get; set; }
    public string Size { get; set; }
}

The point is: it matches both. So if this is an API, you discern by route (/api/tshirts, /api/dogs), and somewhere in your code or configuration you map a route to a class.

You could include this discriminator in your body:

{
    "$type": "Dog",
    "name": "Woof",
    "size": "smol"
}

And then you'd use a hook somewhere to read the $type field and return the appropriate class to deserialize into.

How to do either is another question altogether, and depends on which side of the service you're on (are you writing API controllers or a client for an existing API?) and what frameworks you're using and whatnot.


As for your actual question, you could do something like this:

public void Insert<T>(string json)
{
    var list = System.Text.Json.JsonSerializer
    .Deserialize<IEnumerable<T>>(json);

    var output = services.InsertObject<T>(list);
}

And then call that method through reflection, using a Type obtained from its name.

The amount of horrific code this requires to work should deter you from doing so. If not, see those two questions to write it.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • I know the type field from /api/dog. Question is how to map this "dog" or "cat" string variable and generate a IEnumerable or IEnumerable – netcoredev Oct 08 '21 at 11:31
  • 1
    You can't. You can call a generic method through reflection, but you can't statically declare a generic type with a string variable as type argument. If you know you're in `/api/dog`, then you are in the `DogController` and you know you'll be dealing with a `Dog`, right? Then why do you want to use a string for "Dog"? – CodeCaster Oct 08 '21 at 12:28
  • This is a general controller where all sorts of objects come in.Dont have specific controllers.I know which object type is demanding operations as a string variable.We can find the typeof "Dog" .But how to make a call with this to a generic method ? – netcoredev Oct 08 '21 at 13:27
  • See the last part of my answer, under the horizontal rule. – CodeCaster Oct 08 '21 at 13:28