Azure / Azure/azure-functions-dotnet-worker
Incorrect json returned from polymorphic type when using ASP.NET Core integration
- Dominant language
- C#
- Stars
- 466
- Forks
- 215
- Avg merge
- 3d 10h
- Merged PRs (30d)
- 7
Description
### Description
When using ASP.NET Core integration method that returns IActionResult using OkObjectResult and the return type is a Polymorphic type, some type information seems to be lost.
The problem seems to arise from OkObjectResult storing the "value" as an `object` and thus looses the information about the original type being `WeatherForecastBase` and just serializes the concrete type `WeatherForecastWithCity`.
This is the same as
```CSharp
// Produces json with $type descriminator
var jsonString = JsonSerializer.Serialize(weatherForecastBase);
Console.WriteLine(jsonString);
```
versus
```CSharp
// Produces json without $type descriminator
var jsonString = JsonSerializer.Serialize(weatherForecast);
Console.WriteLine(jsonString);
```
Is this a bug or a feature?
Polymorphic types seems to be supported in all other cases except on the root object.
Is there another way to coerce ASP.NET Core integration into returning the correct type information?
### Steps to reproduce
Sample azure functions based on models from https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism#polymorphic-type-discriminators
```CSharp
[JsonDerivedType(typeof(WeatherForecastBase), typeDiscriminator: "base")]
[JsonDerivedType(typeof(WeatherForecastWithCity), typeDiscriminator: "withCity")]
public class WeatherForecastBase
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
public class WeatherForecastWithCity : WeatherForecastBase
{
public string? City { get; set; }
}
public class WeatherFunctions
{
[Function(nameof(GetWeather))]
[OpenApiOperation(operationId: nameof(GetWeather))]
[OpenApiResponseWithBody(
statusCode: HttpStatusCode.OK,
contentType: "application/json",
bodyType: typeof(WeatherForecastBase),
Description = "Successful operation"
)]
public IActionResult GetWeather([HttpTrigger(AuthorizationLevel.Function, "get", Route = "v1/weather")] HttpRequest request)
{
WeatherForecastBase weatherForecastBase = new WeatherForecastWithCity()
{
City = "Milwaukee",
Date = DateTimeOffset.Parse("2022-09-26T00:00:00-05:00"),
TemperatureCelsius = 16,
Summary = "Cool",
};
return new OkObjectResult(weatherForecastBase);
}
[Function(nameof(GetWeatherV2))]
[OpenApiOperation(operationId: nameof(GetWeatherV2))]
[OpenApiResponseWithBody(
statusCode: HttpStatusCode.OK,
contentType: "application/json",
bodyType: typeof(WeatherForecastBase),
Description = "Successful operation"
)]
public async Task GetWeatherV2([HttpTrigger(AuthorizationLevel.Function, "get", Route = "v2/weather")] HttpRequestData request)
{
WeatherForecastBase weatherForecastBase = new WeatherForecastWithCity()
{
City = "Milwaukee",
Date = DateTimeOffset.Parse("2022-09-26T00:00:00-05:00"),
TemperatureCelsius = 16,
Summary = "Cool",
};
var response = request.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(weatherForecastBase);
return response;
}
}
```
Result from function using ASP.NET Core integration and OkObjectResult. Notice that type discriminator ($type) is missing.
GET v1/weather
{
"city": "Milwaukee",
"date": "2022-09-26T00:00:00-05:00",
"temperatureCelsius": 16,
"summary": "Cool"
}
Result from function using `HttpResponseData.CreateResponse()`. Notice that result correctly contains type discriminator.
v2/weather response:
{
**"$type": "withCity",**
"City": "Milwaukee",
"Date": "2022-09-26T00:00:00-05:00",
"TemperatureCelsius": 16,
"Summary": "Cool"
}
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.