0

I am new to dotnet, trying out dotnet 6 minimal API. I have two models:

namespace Linker.Models
{
    class Link : BaseEntity
    {
        [MaxLength(2048)]
        public string Url { get; set;} = default!;
        [MaxLength(65536)]
        public string? Description { get; set; }
        [Required]
        public User Owner { get; set; } = default!;
        [Required]
        public Space Space { get; set; } = default!;
    }
}

And:

namespace Linker.Models
{
    class Space : BaseEntity
    {
        public string Name { get; set; } = default!;
        public string Code { get; set; } = default!;
        public User Owner { get; set; } = default!;
        public List<Link> Links { get; set; } = new List<Link>();
    }
}

Now when I try to serialize Space model I get error System.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 64. (make sense because Path: $.Links.Space.Links.Space.Links.Space.Links.Space.Links.Space.Links...). Is it posible to prevent dotnet from serializing object this deep? I don't need for dotnet to even try to serialize such a deep relations

Progman
  • 16,827
  • 6
  • 33
  • 48
Bogdan Kuštan
  • 5,427
  • 1
  • 21
  • 30
  • Add a new model without link. – vernou Feb 26 '22 at 10:43
  • Why would I need do that? I have normal models with simple one to may relation, how would third model help in this case? How can I have model relation without relation? – Bogdan Kuštan Feb 26 '22 at 15:48
  • I would suggest that you use two separate models. One in business logic and one to be exposed by the API. Thus, you can precisely define the data expected/returned by the API. – vernou Feb 26 '22 at 17:03
  • The answer is "it depends". Because you can see the circular references yourself. You need to specify what should happen if a circular reference is encountered: that's business logic. So: what's the requirement? – JHBonarius Feb 27 '22 at 13:52
  • @JHBonarius requirement is: when serializing space entity I need to have related `Link` entities, when serializing `Link` entity no need to have `Space` entity. BUT, when deserializing `Link` entity (creating new one), I need to have access to `Space` entity – Bogdan Kuštan Mar 01 '22 at 09:00

3 Answers3

3

You can set ReferenceHandler.Preserve in the JsonSerializerOptions. These docs How to preserve references and handle or ignore circular references in System.Text.Json discuss further.

For manual serialization/deserialization pass the options to the JsonSerializer:

JsonSerializerOptions options = new()
{
    ReferenceHandler = ReferenceHandler.Preserve
};
string serialized = JsonSerializer.Serialize(model, options);

Or to configure globally in minimal API:

using Microsoft.AspNetCore.Http.Json;

var builder = WebApplication.CreateBuilder(args);

// Set the JSON serializer options
builder.Services.Configure<JsonOptions>(options =>
{
    options.SerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
});

You could instead ignore the circular references rather than handling them by using ReferenceHandler.IgnoreCycles. The serializer will set the circular references to null, so there's potential for data loss using this method.

haldo
  • 14,512
  • 5
  • 46
  • 52
  • Thank You. First solution is working, but global configuration is getting ignored – Bogdan Kuštan Feb 28 '22 at 11:23
  • The example for configuring it globally did not work for me. I had to do it as shown on the Microsoft page: https://learn.microsoft.com/en-us/ef/core/querying/related-data/serialization – DannyP Jun 08 '23 at 16:54
2

The reason the global configuration is getting ignored is because the wrong JsonOptions is being used. The following should work:

builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options =>

... rest of code

My default for JsonOptions is Microsoft.AspNetCore.Mvc.JsonOptions, which was not the correct JsonOptions object to change and so globally did not to work.

AJ1000
  • 21
  • 2
0

Try adding [JsonIgnore] before the Space declaration as below:

namespace Linker.Models
{
    class Link : BaseEntity
    {
        [MaxLength(2048)]
        public string Url { get; set;} = default!;
        [MaxLength(65536)]
        public string? Description { get; set; }
        [Required]
        public User Owner { get; set; } = default!;
        [JsonIgnore]
        [Required]
        public Space Space { get; set; } = default!;
    }
}
uuctum
  • 15
  • 4