Reputation: 79
My ASP.NET Core 7 action correctly interprets the type discriminator when deserializing a polymorphic request body.
But it doesn't include the discriminator when serializing a polymorphic response.
Interestingly, the System.Text.Json.JsonSerializer
correctly includes the discriminator if I serialize it myself.
So what do I need to do to get ASP.NET Core to include the discriminator in the response?
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
[JsonDerivedType(typeof(Foo), "Foo")]
[JsonDerivedType(typeof(Bar), "Bar")]
public abstract class Base { }
public class Foo : Base
{
public string? Name { get; set; }
}
public class Bar : Base
{
public string? Name { get; set; }
}
public class Controller : ControllerBase
{
[HttpPost("")]
public Base Action(Base request)
{
return request;
}
}
A request with a discriminator like this is deserialized correctly:
{
"$type": "Bar"
}
But the response from the code above doesn't include the expected discriminator...
{
"name": null
}
Upvotes: 4
Views: 1581
Reputation: 143098
As written in the suggested duplicate - the type which ASP.NET Core resolves for Action
would be the derived one which does not have any polymorphism info, so it will not add any polymorphic serialization data.
To provide appropriate workaround in your case - you can resolve the issue by either manually copying corresponding attributes to the derived types:
[JsonDerivedType(typeof(Foo), "Foo")]
public class Foo : Base
{
public string? Name { get; set; }
}
[JsonDerivedType(typeof(Bar), "Bar")]
public class Bar : Base
{
public string? Name { get; set; }
}
Or by extending the type info provider to copy the needed info from the base one. Simple implementation to get you started (can be improved to automatically analyze the inheritance tree with reflection and copy needed data, for example):
public class JsonHierarchyTypeInfoResolver : DefaultJsonTypeInfoResolver
{
public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
{
var result = base.GetTypeInfo(type, options);
if (type != typeof(Base) && type.IsAssignableTo(typeof(Base)))
{
var baseInfo = base.GetTypeInfo(typeof(Base), options);
foreach (var derivedType in baseInfo.PolymorphismOptions.DerivedTypes)
{
if (derivedType.DerivedType.IsAssignableTo(type))
{
result.PolymorphismOptions ??= new JsonPolymorphismOptions();
result.PolymorphismOptions.DerivedTypes.Add(derivedType);
}
}
}
return result;
}
}
And set it as JsonSerializerOptions.TypeInfoResolver
. For example:
builder.Services.AddControllersWithViews()
.AddJsonOptions(opts => opts.JsonSerializerOptions.TypeInfoResolver = new JsonHierarchyTypeInfoResolver());
Also check out this github issue.
Upvotes: 1