Search code examples
c#jsonasp.net-coresystem.text.jsonminimal-apis

From a minimal ASP.NET Core Web API how do I return a JObject while maintaining the nested elements?


I am building a minimal ASP.NET Core Web API. One of my endpoints needs to return data contained in a JObject. This JObject represents a complex nested data structure. What I am finding is that the response from this endpoint loses all of the nested data from the JObject.

From what have read, I suspect this is caused by the fact that by default .NET Core uses its own Json libraries for serialisation and JObect is a Newtonsoft class, but so far I haven't discovered what the best practice is for handling this.

For example, the JObject that I am working with correctly contains all expected key-value data. Some values contain nested data themselves.

If I run .ToString() on it this is what it looks like:

{
  "key": "stringValue",
  "key": "stringValue",
  "key": "stringValue",
  "key": {
    "nestedKey": "nestedValue",
    "nestedKey": "nestedValue"
  },
  "key": "stringValue",
  "key": "stringValue"
  ..
}

The problem is that when I return the JObject like this:

JObject result = service.ParseToJObject(request.Message);
return TypedResults.Ok(result);

The response object looks like this - all the nested values have been lost.

{
  "key1": [],
  "key2": [],
  "key2": [],
   ...
}

I can resolve this issue with the following change (converting the JObject to a string), but want to know if there is a better way to fix this at the global level. With this change the returned object correctly contains all key-value pairs.

string result = service.ParseToJObject(request.Message).ToString();
return TypedResults.Content(result, "application/json");

I've tried various suggestions off the net around setting Json serialization options on the builder (one example below), but none of these have made any difference to the return data.

builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
    options.SerializerOptions.PropertyNameCaseInsensitive = true;
    options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
    options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});

Thanks


Solution

  • The idiomatic way here is to just switch from using JObject, Minimal APIs do not work with Newtonsoft's Json.NET, they work only with System.Text.Json and this is not configurable and System.Text.Json does not know anything about JObject.

    You have several options here:

    1. Switch to the JsonNode API provided by the System.Text.Json which provides similar capabilities
    2. Write/find custom serializer for System.Text.Json which will handle JObject correctly
    3. Jump some hoops trying to cram in the Json.NET into Minimal APIs - see How to configure NewtonsoftJson with Minimal API in .NET 6.0