I imagine this is a very common scenario, so I thought it worth documenting.
Let’s take the following controller method in a bog standard Asp.Net Core API:
public async Task<DataContainer> Get()
{
var data = Enumerable.Range(1, 5).Select(index => new MyData
{
Value1 = index,
Value2 = "some other value"
})
.ToList();
return new DataContainer()
{
Data = data
};
}
Here’s a method that consumes it:
var client = \_clientFactory.CreateClient();
var result = await client.GetAsync($"https://localhost:22222/endpoint");
using var responseStream = await result.Content.ReadAsStreamAsync();
var data = await JsonSerializer.DeserializeAsync<DataContainer>(responseStream);
So, you’ve expect this to work, right? It makes no difference what DataContainer looks like, because you’re serialising and deserialising using the same technology. In fact, it just returns a correctly structured object, but every value will be null.
I’ll tell you what does work, is this:
var client = \_clientFactory.CreateClient();
var result = await client.GetAsync($"https://localhost:22222/endpoint");
var responseString = await result.Content.ReadAsStringAsync();
var data = Newtonsoft.Json.JsonConvert.DeserializeObject<DataContainer>(responseString);
If you look at the JSON, it looks kosher enough (again, the exact structure is irrelevant). So, what’s the issue?
Interestingly, when the JSON is serialised, it is serialised into camelCase; so if you have a variable such as MyData, it will get serialised as myData. Newtonsoft deals with this, because it’s a common use case in .Net; however, System.Text.Json.Deserialize* assumes that the casing will match the object!
This is odd considering it changes it on the way out!!
Okay, so what’s the fix?
You just tell Deserialize* to use camelCase:
var client = \_clientFactory.CreateClient();
var result = await client.GetAsync($"https://localhost:22222/endpoint");
using var responseStream = await result.Content.ReadAsStreamAsync();
var options = new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
var data = await JsonSerializer.DeserializeAsync<DataContainer>(responseStream, options);
Annoying, eh?