Runtime: Throws an exception on a simple JSON structure return.

Created on 24 Sep 2019  路  14Comments  路  Source: dotnet/runtime

I don't have time to figure out exactly what's buggy about the System.Text.JSON, but this code was working in .NET Core 2.2.

            return this.Ok(
                new
                {
                    deletedEntityTrees,
                    entities,
                    entityTrees,
                    users,
                });

Now I get:

fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HLQ11IPL386G", Request id "0HLQ11IPL386G:00000001": An unhandled exception was thrown by the application.
System.Text.Json.JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32.
at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected(Int32 maxDepth)
at System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer, Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|29_0TFilter,TFilterAsync
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNextTFilter,TFilterAsync
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequestsTContext

When I switch back to NewtonSoft, it works just like it did before the upgrade to .NET Core 3.0.

area-System.Text.Json

Most helpful comment

I ran into a similar issue when deserializing a model with a one to many relationship for a Blazor based web app. To solve the issue we had to add JsonIgnore and a reference to System.Text.Json 4.7.0-preview2.19523.17

...
using System.Text.Json.Serialization;

public class ThingStatus : BaseEntity
    {
        public string Status { get; set; }

        [JsonIgnore] // 
        public List<Thing> Things { get; set; }
    }
 public class Thing: BaseEntity
    {
        public string Name { get; set; }

        public string Description { get; set; }

        public DateTime DueDate { get; set; }

        public ThingStatus ThingStatus { get; set; }

        public int ThingStatusId { get; set; }
    }

```

All 14 comments

JsonSerializer in .NET Core 3.0 does not support circular references. A proposal to support this is being worked on in dotnet/runtime#30820.

If you believe this is not a cycle and instead you just have a very deep heirarchy, you can set JsonSerializerOptions.MaxDepth to something larger than the default.

@GammaFour - can you share what your object graph looks like that you are trying to serialize? Do you have cycles in your object or is it that your graph actually really deep that you are going over the 1_000 max depth?

When I switch back to NewtonSoft, it works just like it did before the upgrade to .NET Core 3.0.

What settings/options do you use with Newtonsoft for it to work?

I ran into a similar issue when deserializing a model with a one to many relationship for a Blazor based web app. To solve the issue we had to add JsonIgnore and a reference to System.Text.Json 4.7.0-preview2.19523.17

...
using System.Text.Json.Serialization;

public class ThingStatus : BaseEntity
    {
        public string Status { get; set; }

        [JsonIgnore] // 
        public List<Thing> Things { get; set; }
    }
 public class Thing: BaseEntity
    {
        public string Name { get; set; }

        public string Description { get; set; }

        public DateTime DueDate { get; set; }

        public ThingStatus ThingStatus { get; set; }

        public int ThingStatusId { get; set; }
    }

```

@timmyreilly Thanks for your suggestion. I had the same problem and JsonIgnore attribute worked for me. I could not find JSonSerializationOptions for reference loop handling in Core 3.0

@JAYARAJ2014 - the reference loop feature is WIP for 5.0, see: https://github.com/dotnet/corefx/issues/41002

Another workaround is to use Microsoft.AspNetCore.Mvc.NewtonsoftJson as NewtonsoftJson is more mature than System.Text.JSON. Example:

services.AddControllersWithViews(opt =>
{
    ....
}
.AddNewtonsoftJson();
// optionally tackle cycles through (opt => opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);

The preview version for ReferenceHandling feature is here.

var options = new JsonSerializerOptions
{
    ReferenceHandling = ReferenceHandling.Preserve
};

string json = JsonSerializer.Serialize(objectWithLoops, options);

Here's the spec doc including samples and notes about many scenarios.
If you want to add feedback, feel free to leave a comment in https://github.com/dotnet/corefx/issues/41002.

@Jozkee, I tried using the alpha from the dotnet5 gallery and it seems to be a prior build number (19523.8) that doesn't yet have the ReferenceHandling property. Do you know what I'm doing wrong?

PS C:\Users\mark\.nuget\packages\system.text.json\5.0.0-alpha1.19523.8\lib\netcoreapp3.0> [System.Reflection.Assembly]::LoadFrom(".\System.Text.Json.dll")
[System.Reflection.Assembly]::LoadFrom(".\System.Text.Json.dll")

GAC    Version        Location
---    -------        --------
False  v4.0.30319     C:\Users\mark\.nuget\packages\system.text.json\5.0.0-alpha1.19523.8\lib\netcoreapp3.0\System.T...

@catfood make sure to select version 5.0.0-alpha.1.20071.1 from the dropdown.

image

@Jozkee the highest version for System.Text.Json that I am seeing is only 4.7.0. I have the Include Prerelease checked.

@GrangeLife You are probably using default package source gallery (nuget.org).

Go to Package Manager Settings and add dotnet5 daily feed to your Package Sources and search there for version 5.0.0-alpha.1.20071.1.

https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json

Hello,
i have installed the alpha versions, after editing my source gallery.
Packages

The ReferenceHandling wont show up in my JsonOptions. I added Jsonoptions for my Controllers.
services.AddControllers().AddJsonOptions(option => { option.JsonSerializerOptions.PropertyNamingPolicy = null; option.JsonSerializerOptions.MaxDepth = 256; });

@erkanKR, it seems that you are using a wrong version. Make sure to select 5.0.0-alpha.1.20071.1. Let me know if it still does not show up.

i have installed the alpha versions, after editing my source gallery.

The versioning schema of the packages on the nightly feed changed a bit. Since we went from alpha1.<version> to alpha.1.<version>, the newest packages don't show up on the top as they are sorted alphabetically (note the extra .). As @Jozkee mentioned, please look at the publish date and make sure to grab 5.0.0-alpha.1.20071.1 or higher to get the feature.

cc @mmitche, @safern

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jamesqo picture jamesqo  路  3Comments

jzabroski picture jzabroski  路  3Comments

jkotas picture jkotas  路  3Comments

nalywa picture nalywa  路  3Comments

matty-hall picture matty-hall  路  3Comments