My previously working code (dotnet 6.0) that was serialising a complex object, stopped producing Json output at the API controller. The object(s) are defined as:
public class ReportResultsDTO
{
public string ReportTitle { get; set; }
public List<ReportResultDTO> Results { get; set; } = [];
}
public class ReportResultDTO
{
public string Title { get; set; }
public List<Dictionary<string, object>> Data;
}
Turns out that because System.Net.Text is now used by default (rather than Newtonsoft.Json) it doesn’t handle converting List<Dictionary<string, object>> to Json. I tried converting it to a Json string first and outputting that, but the returned string was full of \” characters to escape the string(s).
I’m trying to remove the dependency on Newtonsoft throught the code so didn’t really want to have to make the controllers use it just for the sake of one controller. Luckily I found: https://stackoverflow.com/questions/78123885/asp-net-core-3-1-use-newtonsoft-json-only-in-some-controllers
I created a custom ActionFilterAttribute to handle converting the result for the specific controller:
public class ReportResultsJsonFormatterAttribute : ActionFilterAttribute
{
/// <summary>
/// Attribute to override output action to Newtonsoft
/// </summary>
/// <param name="context"></param>
public override void OnActionExecuted(ActionExecutedContext context)
{
if (context.Result is ObjectResult objectResult)
{
IOptions<MvcNewtonsoftJsonOptions> jsonOptions = context.HttpContext.RequestServices.GetService<IOptions<MvcNewtonsoftJsonOptions>>();
objectResult.Formatters.RemoveType<SystemTextJsonOutputFormatter>();
#pragma warning disable CS0618 // Type or member is obsolete
objectResult.Formatters.Add(new NewtonsoftJsonOutputFormatter(
jsonOptions.Value.SerializerSettings,
context.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>(),
context.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>().Value));
#pragma warning restore CS0618 // Type or member is obsolete
}
else
{
base.OnActionExecuted(context);
}
}
}
Which is applied to the controller thus:
[ReportResultsJsonFormatter]
[Produces("application/json")]
[HttpPost("{reportId:int}/execute")]
public async Task<IActionResult> ExecuteReportAsync(int reportId, ReportParameters reportParameters = null, string reportAccessCode = null)
and everything was back to the way it worked previously.