JsonException: A possible object cycle was detected

By FoxLearn 3/13/2025 2:56:52 AM   11
When serializing objects in .NET, you may encounter a JsonException due to circular references between objects.

This can happen when two objects refer to each other, creating an endless loop.

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.

This error can be prevented by handling circular references appropriately.

For example:

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public Order Order { get; set; }
}

public class Order
{
    public string OrderNumber { get; set; }
    public List<Product> Products { get; set; }
}

In this example, an Order contains a list of Product objects, and each Product contains a reference back to the Order.

Solution for .NET 6+ (Using ReferenceHandler.IgnoreCycles)

Starting from .NET 6+, you can avoid this issue by using ReferenceHandler.IgnoreCycles in JsonSerializerOptions. This will ensure that circular references are ignored during serialization:

using System.Text.Json;

var order = new Order
{
    OrderNumber = "ORD123",
    Products = new List<Product>
    {
        new Product { Name = "Laptop", Price = 999.99 },
        new Product { Name = "Mouse", Price = 29.99 }
    }
};

foreach (var product in order.Products)
{
    product.Order = order;  // Circular reference
}

var json = JsonSerializer.Serialize(order, new JsonSerializerOptions()
{
    ReferenceHandler = ReferenceHandler.IgnoreCycles,
    WriteIndented = true
});

Console.WriteLine(json);

With this approach, circular references are ignored, and the serialization will complete successfully without throwing an exception.

Solution for .NET 5 (Using ReferenceHandler.Preserve)

In .NET 5, you can use the ReferenceHandler.Preserve option. This method will include metadata in the serialized JSON to preserve object references:

var json = JsonSerializer.Serialize(order, new JsonSerializerOptions()
{
    ReferenceHandler = ReferenceHandler.Preserve,
    WriteIndented = true
});

Console.WriteLine(json);

This will produce JSON like:

{
  "$id": "1",
  "OrderNumber": "ORD123",
  "Products": [
    {
      "$id": "2",
      "Name": "Laptop",
      "Price": 999.99,
      "Order": { "$ref": "1" }
    },
    {
      "$id": "3",
      "Name": "Mouse",
      "Price": 29.99,
      "Order": { "$ref": "1" }
    }
  ]
}

Solution for Versions Prior to .NET 5 and 6

If you are working with older versions of .NET that don’t support ReferenceHandler, there are still several options to handle circular references.

Option 1: Use Newtonsoft.Json with ReferenceLoopHandling.Ignore

You can use Newtonsoft.Json (Json.NET) to handle circular references with the ReferenceLoopHandling.Ignore option. First, you need to install the Newtonsoft.Json package:

Install-Package Newtonsoft.Json

Then, you can serialize the Order object as follows:

using Newtonsoft.Json;

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

Console.WriteLine(json);

This will generate JSON without circular references.

Option 2: Remove the Circular Reference Property

If a property like Order in the Product class is not crucial, you can simply remove it from serialization by marking it with [JsonIgnore]:

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    
    [JsonIgnore]
    public Order Order { get; set; }  // This will be ignored during serialization
}

Now when you serialize the Order object, the circular reference won’t cause an issue.

Option 3: Create a Custom JsonConverter

For more control over the serialization process, you can create a custom JsonConverter to manually handle the serialization of circular references. For example, here’s how you could serialize the Product class without causing a circular reference:

public class ProductJsonConverter : JsonConverter<Product>
{
    public override Product Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        // Custom deserialization logic here if needed
        return null;
    }

    public override void Write(Utf8JsonWriter writer, Product value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();
        writer.WriteString(nameof(value.Name), value.Name);
        writer.WriteNumber(nameof(value.Price), value.Price);
        // Do not serialize the Order reference
        writer.WriteEndObject();
    }
}

Then, you can use this custom converter during serialization:

var options = new JsonSerializerOptions()
{
    Converters = { new ProductJsonConverter() },
    WriteIndented = true
};

var json = JsonSerializer.Serialize(order, options);
Console.WriteLine(json);

These are a few ways you can deal with circular references during serialization in .NET, tailored to different versions and libraries.