Am using Blazor (Hosted) and looking to preserve referencing when sending results back to client. The sample below doesn't really need reference preservation but is my test scenario for a more complex structure that does.
That payload looks like this:
[
{
"id":"a583baf9-8990-484f-9dc6-e8ea822f49c6",
"name":"Neil",
"themeName":"Blue Gray"
},
{
"id":"a7a8e753-c7af-4b29-9242-7b2f5bdac830",
"name":"Caroline",
"themeName":"Yellow"
}
]
Using
var result = await response.Content.ReadFromJsonAsync<IEnumerable<Staff>>();
I am able to get my Staff objects in the client.
I updated StartUp.cs on server to include:
services.AddControllersWithViews()
.AddJsonOptions(o =>
o.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve
);
Result was that the return payload now looks like this:
{
"$id":"1",
"$values":
[
{
"$id":"2",
"id":"a583baf9-8990-484f-9dc6-e8ea822f49c6",
"name":"Neil",
"themeName":"Blue Gray"
},
{
"$id":"3",
"id":"a7a8e753-c7af-4b29-9242-7b2f5bdac830",
"name":"Caroline",
"themeName":"Yellow"
}
]
}
Seems correct.
This caused JSON deserialisation exception at line:
var result = await response.Content.ReadFromJsonAsync<IEnumerable<Staff>>();
So, I thought I might need to include reference handling options when deserializing on the client as well. So, changed to:
JsonSerializerOptions options = new JsonSerializerOptions();
options.ReferenceHandler = ReferenceHandler.Preserve;
var result = await response.Content.ReadFromJsonAsync<IEnumerable<Staff>>(options);
I got no errors, but my Enumerable included:
The 2 Staff objects (but will all properties nulled). A 3rd null object in the Enumerable.
Could anyone guide me on what I'm doing wrong?
I have found a solution. This is what appeared to be happening:
The default configuration for Json serialization on WebAPI appears to be camel case. However, even though this was the case, I had not had any problem serializing shared classes (that use capitalisation) and deserializing on the client, even though the JSON itself was using camel case.
This started to fail when I added ReferenceHandler.Preserve to my JsonSerializerOptions.
Updating my Json Options as follows, solved the problem:
services.AddControlersWithViews()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
options.JsonSerializerOptions.PropertyNamingPolicy = null // prevent camel case
}
Alternative approach is to use MvcOptions. I don't claim to know which is prefereable, but both the above and the below seem to give the same outcome.
services.AddControllersWithViews(options =>
{
options.OutputFormatters.RemoveType<SystemTextJsonOutputFormatter>();
options.OutputFormatters.Add(new SystemTextJsonOutputFormatter(
new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
ReferenceHandler = ReferenaceHandler.Preserve,
PropertyNamingPolicy = null // prevent camel casing of Json
}));
});
Then on client, when receiving response from WebAPI:
HttpResponseMessage response = await Http.GetAsync(myapiroute);
IEnumerable<Staff> staff = response.Content.ReadFromJsonAsync<IEnumerable<Staff>>(
new JsonSerializerOptions() { ReferenceHandler = ReferenceHandler.Preserve });
And now reference handling appears to cross the boundary from WebAPI to Blazor Client.