Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON.NET Error Self referencing loop detected for type

I tried to serialize POCO class that was automatically generated from Entity Data Model .edmx and when I used

JsonConvert.SerializeObject 

I got the following error:

Error Self referencing loop detected for type System.data.entity occurs.

How do I solve this problem?

like image 222
NevenHuynh Avatar asked Sep 13 '11 05:09

NevenHuynh


People also ask

How do you stop self referencing?

ProxyCreationEnabled = False code That will get rid of 'Self referencing loop' error in your json output from webapi.

What is a self referencing loop?

And for Newtonsoft.Json (Or JSON.NET as it's sometimes called, default for .NET Core 2.2 and lower) : JsonSerializationException: Self referencing loop detected with type. They mean essentially the same thing, that you have two models that reference each other and will cause an infinite loop of serializing doom.

What is reference loop handling?

Ignore : Json.NET will ignore objects in reference loops and not serialize them. The first time an object is encountered it will be serialized as usual but if the object is encountered as a child object of itself the serializer will skip serializing it. ReferenceLoopHandling.

What is Jsonconvert SerializeObject C#?

SerializeObject Method (Object, Type, JsonSerializerSettings) Serializes the specified object to a JSON string using a type, formatting and JsonSerializerSettings. Namespace: Newtonsoft.Json.


4 Answers

That was the best solution https://learn.microsoft.com/en-us/archive/blogs/hongyes/loop-reference-handling-in-web-api

Fix 1: Ignoring circular reference globally

(I have chosen/tried this one, as have many others)

The json.net serializer has an option to ignore circular references. Put the following code in WebApiConfig.cs file:

 config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling 
= Newtonsoft.Json.ReferenceLoopHandling.Ignore; 

The simple fix will make serializer to ignore the reference which will cause a loop. However, it has limitations:

  • The data loses the looping reference information
  • The fix only applies to JSON.net
  • The level of references can't be controlled if there is a deep reference chain

If you want to use this fix in a non-api ASP.NET project, you can add the above line to Global.asax.cs, but first add:

var config = GlobalConfiguration.Configuration;

If you want to use this in .Net Core project, you can change Startup.cs as:

  var mvc = services.AddMvc(options =>
        {
           ...
        })
        .AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

Fix 2: Preserving circular reference globally

This second fix is similar to the first. Just change the code to:

config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling 
     = Newtonsoft.Json.ReferenceLoopHandling.Serialize;     
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling 
     = Newtonsoft.Json.PreserveReferencesHandling.Objects;

The data shape will be changed after applying this setting.

[
   {
      "$id":"1",
      "Category":{
         "$id":"2",
         "Products":[
            {
               "$id":"3",
               "Category":{
                  "$ref":"2"
               },
               "Id":2,
               "Name":"Yogurt"
            },
            {
               "$ref":"1"
            }
         ],
         "Id":1,
         "Name":"Diary"
      },
      "Id":1,
      "Name":"Whole Milk"
   },
   {
      "$ref":"3"
   }
]

The $id and $ref keeps the all the references and makes the object graph level flat, but the client code needs to know the shape change to consume the data and it only applies to JSON.NET serializer as well.

Fix 3: Ignore and preserve reference attributes

This fix is decorate attributes on model class to control the serialization behavior on model or property level. To ignore the property:

 public class Category 
    { 
        public int Id { get; set; } 
        public string Name { get; set; } 
       
        [JsonIgnore] 
        [IgnoreDataMember] 
        public virtual ICollection<Product> Products { get; set; } 
    } 

JsonIgnore is for JSON.NET and IgnoreDataMember is for XmlDCSerializer. To preserve reference:

 // Fix 3 
        [JsonObject(IsReference = true)] 
        public class Category 
        { 
            public int Id { get; set; } 
            public string Name { get; set; } 
         
           // Fix 3 
           //[JsonIgnore] 
           //[IgnoreDataMember] 
           public virtual ICollection<Product> Products { get; set; } 
       } 
        
       [DataContract(IsReference = true)] 
       public class Product 
       { 
           [Key] 
           public int Id { get; set; } 
        
           [DataMember] 
           public string Name { get; set; } 
        
           [DataMember] 
           public virtual Category Category { get; set; } 
       }

JsonObject(IsReference = true)] is for JSON.NET and [DataContract(IsReference = true)] is for XmlDCSerializer. Note that: after applying DataContract on class, you need to add DataMember to properties that you want to serialize.

The attributes can be applied on both json and xml serializer and gives more controls on model class.

like image 95
Bishoy Hanna Avatar answered Oct 15 '22 07:10

Bishoy Hanna


Use JsonSerializerSettings

  • ReferenceLoopHandling.Error (default) will error if a reference loop is encountered. This is why you get an exception.
  • ReferenceLoopHandling.Serialize is useful if objects are nested but not indefinitely.
  • ReferenceLoopHandling.Ignore will not serialize an object if it is a child object of itself.

Example:

JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented, 
new JsonSerializerSettings 
{ 
        ReferenceLoopHandling = ReferenceLoopHandling.Serialize
});

Should you have to serialize an object that is nested indefinitely you can use PreserveObjectReferences to avoid a StackOverflowException.

Example:

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

Pick what makes sense for the object you are serializing.

Reference http://james.newtonking.com/json/help/

like image 35
DalSoft Avatar answered Oct 15 '22 07:10

DalSoft


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(YourObject, Formatting.Indented,
    new JsonSerializerSettings() {
        ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
    }
);

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

like image 40
smockle Avatar answered Oct 15 '22 05:10

smockle


The simplest way to do this is to install Json.NET from nuget and add the [JsonIgnore] attribute to the virtual property in the class, for example:

    public string Name { get; set; }
    public string Description { get; set; }
    public Nullable<int> Project_ID { get; set; }

    [JsonIgnore]
    public virtual Project Project { get; set; }

Although these days, I create a model with only the properties I want passed through so it's lighter, doesn't include unwanted collections, and I don't lose my changes when I rebuild the generated files...

like image 30
Sam Jones Avatar answered Oct 15 '22 07:10

Sam Jones