.Net Core 3.0 Possible Object Cycle Was Detected Which Is Not Supported

.Net Core 3.0 possible object cycle was detected which is not supported

I have tried your code in a new project and the second way seems to work well after installing the package Microsoft.AspNetCore.Mvc.NewtonsoftJson firstly for 3.0

services.AddControllersWithViews()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);

Try with a new project and compare the differences.

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

this is happening because your data have a reference loop.

e.g

// this example creates a reference loop
var p = new Product()
{
ProductCategory = new ProductCategory()
{ products = new List<Product>() }
};
p.ProductCategory.products.Add(p); // <- this create the loop
var x = JsonSerializer.Serialize(p); // A possible object cycle was detected ...

You can not handle the reference loop situation in the new System.Text.Json yet (netcore 3.1.1) unless you completely ignore a reference and its not a good idea always. (using [JsonIgnore] attribute)

but you have two options to fix this.

  1. you can use Newtonsoft.Json in your project instead of System.Text.Json (i linked an article for you)

  2. Download the System.Text.Json preview package version 5.0.0-alpha.1.20071.1 from dotnet5 gallery (through Visual Studio's NuGet client):

option 1 usage:

services.AddMvc()
.AddNewtonsoftJson(
options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
// if you not using .AddMvc use these methods instead
//services.AddControllers().AddNewtonsoftJson(...);
//services.AddControllersWithViews().AddNewtonsoftJson(...);
//services.AddRazorPages().AddNewtonsoftJson(...);

option 2 usage:

// for manual serializer
var options = new JsonSerializerOptions
{
ReferenceHandling = ReferenceHandling.Preserve
};

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

// -----------------------------------------
// for asp.net core 3.1 (globaly)
services.AddMvc()
.AddJsonOptions(o => {
o.JsonSerializerOptions
.ReferenceHandling = ReferenceHandling.Preserve
});

these serializers have ReferenceLoopHandling feature.

  • Edit : ReferenceHandling changed to ReferenceHandler in DotNet 5

but if you decide to just ignore one reference use [JsonIgnore] on one of these properties. but it causes null result on your API response for that field even when you don't have a reference loop.

public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }

public int ProductCategoryId { get; set; }
// [JsonIgnore] HERE or
public virtual ProductCategory ProductCategory { get; set; }
}

public class ProductCategory
{
public int Id { get; set; }
// [JsonIgnore] or HERE
public ICollection<Product> products {get;set;}
}

Error when deserialize Json .NET CORE 5 - JsonException: A possible object cycle was detected which is not supported

I suggest to switch to the Newtonsoft.Json to handle the circular references problem.

1.Install NewtonsoftJson package.

Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson

2.Add and configure it in ConfigureServices.

services.AddControllers().AddNewtonsoftJson(options=>
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);

3.Use JsonConvert to Deserialize.

client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json"));
client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter");

var streamTask = await client.GetStringAsync("https://api.github.com/orgs/dotnet/repos?sort=created&per_page=5&direction=desc");
var repositories = JsonConvert.DeserializeObject<List<Repository>>(streamTask);

How to resolve System.Text.Json.JsonException: A possible object cycle was detected in Entity Framework?

The reason you're getting this is because you're serializing an object graph that has a cycle, and this is typical of an EF DB entity graph

Imagine you have a Person with one Profile, and that Profile has a Person (a link back to the parent Person)

It is possible to write C# code like:

myPerson.Profiles.First().Person.Profiles.First().Person.Profiles.First().Person.Profiles.First().Person.Profiles.First().Person..

You can go on like that forever..

..and that is what the json serializer is also doing when it is trying to serialize every property of every profile

You have a few options..

  • You can modify the graph before you serialize it: set the Person of every Profile (or whatever level N property is the first property to link back to an earlier N-x one) to null to stop the cycle

  • You can serialize an object graph that doesn't have cycles, like a set of DTOs you map your EF entities onto before you serialize

  • You can tell Newtonsoft to watch the references and not set anything it saw before:

string json = JsonConvert.SerializeObject(joe, Formatting.Indented, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});

Most people would, I say, avoid ever seeing this because they do option 2; generally we don't serialize our DB entities, we have a set of classes that are specifically designed for our front end to use, they might look different etc; it's rare for a system's front end to fully and exactly need data objects that look exactly like what the DB is modelling. If those front end focused objects don't have cycles (a person might have a list of profiles but there isn't any need for the profile to link back, because it's used too down as the hi is drawn) they don't cause this problem.

Be careful with option 1; if you call save on your context (for any reason) after you modified the graph to be serializer friendly, you'll could end up disconnecting relationships in the db. 3 is a bit of a kludge, a work around for the problem caused by not having done 2, but 2 is more effort to go to

System.Text.Json.JsonException: 'A possible object cycle was detected which is not supported....'

You have encountered a couple problems here.

Firstly, you need to increase MaxDepth from 5 to 6:

var options = new JsonSerializerOptions
{
WriteIndented = true,
MaxDepth = 6 // Fixed
};
jsonString = JsonSerializer.Serialize(pkg, options);

Demo fiddle #1 here.

The JSON you are trying to serialize looks like this:

{                                               // Level 1
"Steps": [ // Level 2
{ // Level 3
"Name": "stepTestName",
"PrecedenceConstraints": [ // Level 4
{ // Level 5
"SourceStepName": "stepTestName", // THESE PROPERTY VALUES
"ConstraintValue": 1, // ARE APPARENTLY LEVEL 6.
"ConstraintMet": false
}
]
}
],
"Name": "packageTestName"
}

It seems as though the primitive property values in the PrecedenceConstraints objects count as an extra level. If I comment out its properties I can serialize your data model at MaxDepth = 5:

{
"Steps": [
{
"Name": "stepTestName",
"PrecedenceConstraints": [
{} // No properties so level maxes out at 5, apparently.
]
}
],
"Name": "packageTestName"
}

Demo fiddle #2 here demonstrating this. (The documentation doesn't explain the precise meaning of MaxDepth.)

Secondly, your PrecedenceConstraint lacks a public, parameterless constructor. As explained in the documentation How to migrate from Newtonsoft.Json to System.Text.Json : Deserialize to immutable classes and structs, deserialization of such types is not supported out of the box:

System.Text.Json supports only public parameterless constructors. As a workaround, you can call a constructor with parameters in a custom converter.

This prevents your data model from being deserialize successfully. One fix is to add a parameterless constructor as required by the documentation:

public class PrecedenceConstraint
{
private string _sourceStepName;
private StepPrecedenceValue _constraintValue;
private bool _constraintMet;

public PrecedenceConstraint() { } // FIXED added parameterless constructor as required by System.Text.Json

// Remainder unchanged.

Now your data model can be round-tripped at MaxDepth = 6. Demo fiddle #3 here.



Related Topics



Leave a reply



Submit