204

I have a mssql database for my website within 4 tables.

When I use this:

public static string GetAllEventsForJSON()
{
    using (CyberDBDataContext db = new CyberDBDataContext())
    {
        return JsonConvert.SerializeObject((from a in db.Events where a.Active select a).ToList(), new JavaScriptDateTimeConverter());
    }
}

The code results in the following error:

Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'CyberUser' with type 'DAL.CyberUser'. Path '[0].EventRegistrations[0].CyberUser.UserLogs[0]'.

Ashley Medway
  • 7,151
  • 7
  • 49
  • 71
PassionateDeveloper
  • 14,558
  • 34
  • 107
  • 176

13 Answers13

355

I just had the same problem with Parent/Child collections and found that post which has solved my case. I Only wanted to show the List of parent collection items and didn't need any of the child data, therefore i used the following and it worked fine:

JsonConvert.SerializeObject(ResultGroups, Formatting.None,
                        new JsonSerializerSettings()
                        { 
                            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                        });

JSON.NET Error Self referencing loop detected for type

it also referes to the Json.NET codeplex page at:

http://json.codeplex.com/discussions/272371

Documentation: ReferenceLoopHandling setting

  • In WebAPI OData v4, I found that some types of data required both the ReferenceLoopHandling.Ignore and PreserveReferencesHandling.Objects – Chris Schaller Jun 15 '18 at 05:50
  • 1
    Sings *Allelluiah* Thanks so much only up-voting by 1 is not sufficient – JP Chapleau Jul 09 '18 at 12:56
  • Worked for me for Entity Framework Core on Blazor Server. – David Jones Mar 04 '21 at 02:46
  • Thanks! For my project, I've decided to include this as configuration in my startup.cs. – Medismal Apr 26 '21 at 09:13
  • 2
    Even using the "ignore" setting newtonsoft seems to partially serialize the self referencing. In my code, it seems the serialized data is about 80x bigger than the real data (when serialized). – Jason Cheng Jun 30 '21 at 15:43
  • Note that if you're using Entity Framework models, you'll still have issues despite these settings. You can create extension methods to convert the entities to their view model counterparts and those should work fine – Alex Jul 13 '22 at 16:31
54

The fix is to ignore loop references and not to serialize them. This behaviour is specified in JsonSerializerSettings.

Single JsonConvert with an overload:

JsonConvert.SerializeObject((from a in db.Events where a.Active select a).ToList(), Formatting.Indented,
    new JsonSerializerSettings() {
        ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
    }
);

If you'd like to make this the default behaviour, add a Global Setting with code in Application_Start() in Global.asax.cs:

JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
     Formatting = Newtonsoft.Json.Formatting.Indented,
     ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
};

Reference: https://github.com/JamesNK/Newtonsoft.Json/issues/78

smockle
  • 2,162
  • 17
  • 19
  • 4
    Serialization with this takes a very long time for me – daniel Jan 28 '15 at 22:55
  • This doesn't seem to work when the object with circular loops are NHibernate model POCOs (in that case the serialization retrieves a ton of garbage, or sometimes just times out). – Fernando Gonzalez Sanchez Sep 08 '18 at 00:08
  • "IsSecuritySafeCritical":false,"IsSecurityTransparent":false, "MethodHandle":{"Value":{"value":140716810003120}},"Attributes":150,"CallingConvention":1, "ReturnType":"System.Void, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", "ReturnTypeCustomAttributes":{"ParameterType":"System.Void, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e","Name":null, "HasDefaultValue":true,"DefaultValue":null,"RawDefaultValue":null,"MetadataToken":134217728,"Attributes":0,"Position":-1,"IsIn":false,"IsLcid":false,. ... etc. – Fernando Gonzalez Sanchez Sep 08 '18 at 00:09
  • 1
    Watch out... if the references are truly circular, using `ReferenceLoopHandling.Ignore` can cause a stack overflow. – Gyromite Feb 09 '22 at 15:27
45

If using ASP.NET Core MVC, add this to the ConfigureServices method of your startup.cs file:

services.AddMvc()
    .AddJsonOptions(
        options => options.SerializerSettings.ReferenceLoopHandling =            
        Newtonsoft.Json.ReferenceLoopHandling.Ignore
    );
Robert Moskal
  • 21,737
  • 8
  • 62
  • 86
andreisrob
  • 1,615
  • 15
  • 11
13

This may help you.

public MyContext() : base("name=MyContext") 
{ 
    Database.SetInitializer(new MyContextDataInitializer()); 
    this.Configuration.LazyLoadingEnabled = false; 
    this.Configuration.ProxyCreationEnabled = false; 
} 

http://code.msdn.microsoft.com/Loop-Reference-handling-in-caaffaf7

ddagsan
  • 1,786
  • 18
  • 21
  • 4
    This is the best way to approach it if you're also using async methods. It can be a real pain, but it solves a lot of issues you would have otherwise (including this one) and also can be much more performant as you're only querying what you will use. – Josh McKearin Jun 29 '15 at 06:09
  • In your xyz.edmx, open the xyz.Context.vb file which will be hidden by default. This will have `code` Public Sub New() Mybase.New("name=EntityConName") End Sub `code`. Now before End Sub add `code` Me.Configuration.LazyLoadingEnabled = False Me.Configuration.ProxyCreationEnabled = False `code` That will get rid of 'Self referencing loop' error in your json output from webapi. – Venkat May 14 '17 at 19:38
  • I found this didn't work for me. I used AsNoTracking() and it fixed it. Maybe help someone else – scottsanpedro Feb 20 '20 at 12:24
  • @scottsanpedro it was better if we could see your code. – ddagsan Feb 21 '20 at 13:00
10

You must set Preserving Object References:

var jsonSerializerSettings = new JsonSerializerSettings
{
    PreserveReferencesHandling = PreserveReferencesHandling.Objects
};

Then call your query var q = (from a in db.Events where a.Active select a).ToList(); like

string jsonStr = Newtonsoft.Json.JsonConvert.SerializeObject(q, jsonSerializerSettings);

See: https://www.newtonsoft.com/json/help/html/PreserveObjectReferences.htm

Matt
  • 25,467
  • 18
  • 120
  • 187
Cyrus
  • 2,261
  • 2
  • 22
  • 37
10

I am using Dot.Net Core 3.1 and did an search for

"Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property "

I am adding this to this question, as it will be an easy reference. You should use the following in the Startup.cs file:

 services.AddControllers()
                .AddNewtonsoftJson(options =>
                {
                    // Use the default property (Pascal) casing
                    options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                    options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                });
krishnan2784
  • 101
  • 1
  • 2
7

for asp.net core 3.1.3 this worked for me

services.AddControllers().AddNewtonsoftJson(opt=>{
            opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        });
Karim Tingdis
  • 180
  • 3
  • 7
6

Add "[JsonIgnore]" to your model class

{
  public Customer()
  {
    Orders = new Collection<Order>();
  }

public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }

[JsonIgnore]
public ICollection<Order> Orders { get; set; }
}
Samet Sunman
  • 329
  • 1
  • 4
  • 11
2

The JsonSerializer instance can be configured to ignore reference loops. Like in the following, this function allows to save a file with the content of the json serialized object:

    public static void SaveJson<T>(this T obj, string FileName)
    {
   
       JsonSerializer serializer = new JsonSerializer();
        serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        using (StreamWriter sw = new StreamWriter(FileName))
        {
            using (JsonWriter writer = new JsonTextWriter(sw))
            {
                writer.Formatting = Formatting.Indented;
                serializer.Serialize(writer, obj);
            }
        }
    }
Alex Alvarez
  • 371
  • 3
  • 6
2

If you are like me and were previously calling SerializeObject with a converter, you will need to remove the converter parameter and add it to your config... Like so:

var isoConvert = new IsoDateTimeConverter();
isoConvert.DateTimeFormat = _dateFormat;
List<JsonConverter> converters = new List<JsonConverter>();
converters.Add(isoConvert);
JsonSerializerSettings settings = new JsonSerializerSettings()
{
    PreserveReferencesHandling = PreserveReferencesHandling.Objects,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    Converters = converters
};

// Old Code:
//response.Write(JsonConvert.SerializeObject(Data, isoConvert);
response.Write(JsonConvert.SerializeObject(Data, settings));
birwin
  • 2,524
  • 2
  • 21
  • 41
1

JsonConvert.SerializeObject(ObjectName, new JsonSerializerSettings(){ PreserveReferencesHandling = PreserveReferencesHandling.Objects, Formatting = Formatting.Indented });

user3824027
  • 440
  • 3
  • 10
  • 8
    While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. – Alex Riabov Dec 12 '18 at 09:11
1

Sometimes you have loops becouse your type class have references to other classes and that classes have references to your type class, thus you have to select the parameters that you need exactly in the json string, like this code.

List<ROficina> oficinas = new List<ROficina>();
oficinas = /*list content*/;
var x = JsonConvert.SerializeObject(oficinas.Select(o => new
            {
                o.IdOficina,
                o.Nombre
            }));
1

Make sure you aren't missing any await keywords

You can get this error if you make a silly mistake like forgetting to await an asynchronous method.

    public async Task<JsonResult> GetTaxTable([FromServices] TaxService taxService)
    {
        var taxTable = /* await */ taxService.GetTaxTable();
        return new JsonResult(taxTable);
    }

Given that GetTaxTable is an async function, if you forget to await it you'll inadvertently end up passing a Task to the JsonResult constructor - and a Task isn't serializable so while it may at first look like everything just blew up on you - the simple solution is just to add await.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689