How do you pass a derived JObject class in the body of a Web Api

Viewed 798

If I have the class

public class TestMe : JObject
{

}

How do I consume it within an API method?

public async Task<IHttpActionResult> UpdateMe(int accountId, [FromBody] TestMe sometest)
{
  Console.WriteLine(sometest);
  ...

When I try posting anything for the body, ex:

{
  "Bye": "string"
}

I get the following error before the Console.WriteLine line in the UpdateMe method is even hit:

"The parameters dictionary contains an invalid entry for parameter 'sometest' for method 'System.Threading.Tasks.Task`1[System.Web.Http.IHttpActionResult] UpdateMe(Int32, TestMe)' in '<somepath>.Controllers.MiscController'. The dictionary contains a value of type 'Newtonsoft.Json.Linq.JObject', but the parameter requires a value of type 'TestMe'."
2 Answers

the controller can't accept a class that inherits directly from the JObject class. (more info here). I assume that in your case you don't know what properties you will receive in your input JSON. These are possible solutions:

Accept only the JObject class in your controller.

[Route("{accountID}")]
[HttpPost]
public async Task<IHttpActionResult> UpdateMe(int accountId, [FromBody] JObject sometest)
{
    string test = sometest.ToString();

    return Ok(test);
}

you can work with the JObject class as you wish. Example - read more here.
This is one solution to the problem.


Another solution will be to read the JSON body as a raw JSON and do it whatever your needs may be:

[Route("{accountID}")]
[HttpPost]
public async Task<IHttpActionResult> UpdateMe(int accountId)
{
    string rawContent = string.Empty;
    using (var contentStream = await this.Request.Content.ReadAsStreamAsync())
    {
        contentStream.Seek(0, SeekOrigin.Begin);
        using (var sr = new StreamReader(contentStream))
        {
            rawContent = sr.ReadToEnd();
            // use raw content here
        }
    }

    return Ok(rawContent);
}

Another solution for an unknown input JSON is to accept the dynamic type in the controller, but personally, I don't recommend that. How to implement it read here. Why I don't like it, check this here.


The short answer NOT possible, IMO what you are asking for is NOT possible and has not thing to do with api.

IMO you have 3 options either use

  • dynamic type
  • custom object type
  • Dictionay type

As this answer mention,

Whatever is the reason you want to do that - the reason is simple: JObject implements IDictionary and this case is treated in a special way by Json.NET. If your class implements IDictionary - Json.NET will not look at properties of your class but instead will look for keys and values in the dictionary. So to fix your case you can do this

Let's get some details:

To prove my first point, try just to create a simple console app with newton.json with your input as string:

var input = "{\"Bye\": \"string\"}";

With dynamic it will works fine:

var result = JsonConvert.DeserializeObject<dynamic>(input);
Console.WriteLine(JsonConvert.SerializeObject(result));

Now with customize object like:

public class TestMe
{
    public string Bye { get; set; }
}

and

var result = JsonConvert.DeserializeObject<TestMe>(input);
Console.WriteLine(JsonConvert.SerializeObject(result));

Works fine as well.

Now lets take your approach:

public class TestMe : JObject
{
    
}

Testing it with following, it will break:

var result = JsonConvert.DeserializeObject<TestMe>(input);
Console.WriteLine(JsonConvert.SerializeObject(result));

Now lets try with Dictionary:

public class TestMe
{
    [JsonExtensionData]
    public Dictionary<string, JToken> MyProperties { get; set; } = new Dictionary<string, JToken>();
}

And test it

var result = JsonConvert.DeserializeObject<TestMe>(input);
Console.WriteLine(JsonConvert.SerializeObject(result));

Will also works fine.

Now I have presented 3 options.

Related