JSON.NET Error Self referencing loop detected for type

后端 未结 25 2853
我在风中等你
我在风中等你 2020-11-22 02:16

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

JsonConvert.SerializeObject 

相关标签:
25条回答
  • 2020-11-22 02:21

    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.

    0 讨论(0)
  • 2020-11-22 02:22

    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

    0 讨论(0)
  • 2020-11-22 02:24

    In .NET Core 1.0, you can set this as a global setting in your Startup.cs file:

    using System.Buffers;
    using Microsoft.AspNetCore.Mvc.Formatters;
    using Newtonsoft.Json;
    
    // beginning of Startup class
    
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(options =>
            {
                options.OutputFormatters.Clear();
                options.OutputFormatters.Add(new JsonOutputFormatter(new JsonSerializerSettings(){
                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                }, ArrayPool<char>.Shared));
            });
        }
    
    0 讨论(0)
  • 2020-11-22 02:25

    Team:

    This works with ASP.NET Core; The challenge to the above is how you 'set the setting to ignore'. Depending on how you setup your application it can be quite challenging. Here is what worked for me.

    This can be placed in your public void ConfigureServices(IServiceCollection services) section.

    services.AddMvc().AddJsonOptions(opt => 
            { 
          opt.SerializerSettings.ReferenceLoopHandling =
          Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            });
    
    0 讨论(0)
  • 2020-11-22 02:27

    I've inherited a database application that serves up the data model to the web page. Serialization by default will attempt to traverse the entire model tree and most of the answers here are a good start on how to prevent that.

    One option that has not been explored is using interfaces to help. I'll steal from an earlier example:

    public partial class CompanyUser
    {
        public int Id { get; set; }
        public int CompanyId { get; set; }
        public int UserId { get; set; }
    
        public virtual Company Company { get; set; }
    
        public virtual User User { get; set; }
    }
    
    public interface IgnoreUser
    {
        [JsonIgnore]
        User User { get; set; }
    }
    
    public interface IgnoreCompany
    {
        [JsonIgnore]
        User User { get; set; }
    }
    
    public partial class CompanyUser : IgnoreUser, IgnoreCompany
    {
    }
    

    No Json settings get harmed in the above solution. Setting the LazyLoadingEnabled and or the ProxyCreationEnabled to false impacts all your back end coding and prevents some of the true benefits of an ORM tool. Depending on your application the LazyLoading/ProxyCreation settings can prevent the navigation properties loading without manually loading them.

    Here is a much, much better solution to prevent navigation properties from serializing and it uses standard json functionality: How can I do JSON serializer ignore navigation properties?

    0 讨论(0)
  • 2020-11-22 02:28

    To serialize usin NEWTONSOFTJSON when you have loop issue, in my case I did not need modify global.asax or either apiconfig. I just use JsonSerializesSettings ignoring Looping handling.

    JsonSerializerSettings jss = new JsonSerializerSettings();
    jss.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
    var lst = db.shCards.Where(m => m.CardID == id).ToList();
    string json = JsonConvert.SerializeObject(lst, jss);
    
    0 讨论(0)
提交回复
热议问题