When you send a request to ASP.NET with a JSON body, you get the following exception:
System.Text.Json.JsonException: The JSON value could not be converted to System.String. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
You can’t deserialize JSON to a string. You’re either using a string parameter with [FromBody] or your model has a string property that maps to a nested JSON object. Instead of using a string, you have two options:
- Use JsonElement.
- Deserialize the request body to the proper type yourself.
I’ll show examples below.
Option 1 – Use JsonElement instead of string
Instead of using a string parameter (or string property in a model), use JsonElement. When the request comes in, the JSON request body will be loaded into the JsonElement object. Here’s an example:
using System.Text.Json;
[HttpPost()]
public IActionResult Post([FromBody]JsonElement jsonElement)
{
//Look at a property
_logger.LogInformation($"Name={jsonElement.GetProperty("Name")}");
return Ok();
}
Code language: C# (cs)
You can use JsonElement to look at properties (as shown above) or deserialize to a specific type (if you’re passing a “type discriminator” in the request). Here’s an example:
if (jsonElement.TryGetProperty("Type", out JsonElement type)
&& type.GetString() == "Person")
{
Person person = jsonElement.Deserialize<Person>();
}
Code language: C# (cs)
Option 2 – Deserialize the JSON request body yourself
The framework tries to deserialize the request body when you’re using a body parameter (with or without [FromBody]). It needs to know the type to deserialize to. This is a problem if you don’t know the type until runtime – such as when you’re passing in a “type discriminator”.
You can solve this by deserializing the request body yourself. To do this, remove the body parameter, and then deserialize the Request.Body stream to the right type. Here’s an example of how to do this:
using System.Text.Json;
[HttpPost()]
public async Task<IActionResult> Post([FromHeader] string type)
{
if (type == "Person")
{
Person person = await JsonSerializer.DeserializeAsync<Person>(Request.Body);
_logger.LogInformation($"Got person named {person.Name}");
}
return Ok();
}
Code language: C# (cs)
Note: You *must* use DeserializeAsync(), not Deserialize(), because you can’t do synchronous IO by default.
When the request comes in, this code looks at the “type discriminator” header and uses it to deserialize the request body to the correct type.