web-dev-qa-db-fra.com

Erreur JSON.NET Une boucle à référence automatique détectée pour le type

J'ai essayé de sérialiser la classe POCO qui a été générée automatiquement à partir de Entity Data Model .edmx et quand j'ai utilisé

JsonConvert.SerializeObject 

J'ai eu l'erreur suivante:

Erreur Une boucle d'auto-référencement détectée pour le type System.data.entity se produit.

Comment résoudre ce problème?

437
NevenHuynh

C'était la meilleure solution https://code.msdn.Microsoft.com/Loop-Reference-handling-in-caaffaf7

Correctif 1: Ignorer la référence circulaire globalement

(j'ai choisi/essayé celui-ci, comme beaucoup d'autres)

Le sérialiseur json.net a une option pour ignorer les références circulaires. Placez le code suivant dans le fichier WebApiConfig.cs:

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

Le correctif simple fera que le sérialiseur ignore la référence, ce qui provoquera une boucle. Cependant, il a des limites:

  • Les données perdent les informations de référence en boucle
  • Le correctif s'applique uniquement à JSON.net
  • Le niveau de références ne peut pas être contrôlé s'il existe une chaîne de référence profonde

Si vous souhaitez utiliser ce correctif dans un projet ASP.NET non-api, vous pouvez ajouter la ligne ci-dessus à Global.asax.cs, mais ajoutez d'abord:

var config = GlobalConfiguration.Configuration;

Si vous souhaitez utiliser cela dans le projet . Net Core , vous pouvez modifier Startup.cs en tant que:

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

Solution 2: Conserver la référence circulaire globalement

Ce deuxième correctif est similaire au premier. Il suffit de changer le code en:

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

La forme des données sera modifiée après l'application de ce paramètre.

[
   {
      "$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"
   }
]

$ Id et $ ref conservent toutes les références et rendent le niveau de graphe d'objet plat, mais le code client a besoin de connaître le changement de forme pour consommer les données et s'applique également au sérialiseur JSON.NET.

Correction 3: Ignorer et préserver les attributs de référence

Ce correctif consiste à décorer les attributs de la classe de modèle pour contrôler le comportement de la sérialisation au niveau du modèle ou de la propriété. Pour ignorer la propriété:

 public class Category 
    { 
        public int Id { get; set; } 
        public string Name { get; set; } 

        [JsonIgnore] 
        [IgnoreDataMember] 
        public virtual ICollection<Product> Products { get; set; } 
    } 

JsonIgnore est pour JSON.NET et IgnoreDataMember est pour XmlDCSerializer. Pour conserver la référence:

 // 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 pour JSON.NET et [DataContract(IsReference = true)] est pour XmlDCSerializer. Notez que: après avoir appliqué DataContract à la classe, vous devez ajouter DataMember aux propriétés que vous souhaitez sérialiser.

Les attributs peuvent être appliqués sur les sérialiseurs json et xml et donnent plus de contrôles sur la classe de modèle.

418
Bishoy Hanna

Utilisez JsonSerializerSettings

  • ReferenceLoopHandling.Error (valeur par défaut) générera une erreur si une boucle de référence est rencontrée. C'est pourquoi vous obtenez une exception.
  • ReferenceLoopHandling.Serialize est utile si les objets sont imbriqués mais pas indéfiniment.
  • ReferenceLoopHandling.Ignore ne sérialisera pas un objet s'il s'agit d'un objet enfant de lui-même.

Exemple:

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

Si vous devez sérialiser un objet imbriqué indéfiniment, vous pouvez utiliser PreserveObjectReferences pour éviter une exception StackOverflowException.

Exemple:

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

Choisissez ce qui a du sens pour l'objet que vous sérialisez.

Référence http://james.newtonking.com/json/help/

433
DalSoft

Le correctif consiste à ignorer les références de boucle et non à les sérialiser. Ce comportement est spécifié dans JsonSerializerSettings.

Unique JsonConvert avec une surcharge:

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

Paramètre global avec un code dans Application_Start() dans Global.asax.cs:

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

Référence: https://github.com/JamesNK/Newtonsoft.Json/issues/78

48
smockle

La façon la plus simple de procéder consiste à installer Json.NET à partir de nuget et à ajouter l'attribut [JsonIgnore] à la propriété virtuelle de la classe, par exemple:

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

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

Bien que ces jours-ci, je crée un modèle avec uniquement les propriétés que je souhaite transmettre; il est donc plus clair, n'inclut pas les collections non désirées et je ne perds pas mes modifications lorsque je reconstruis les fichiers générés ...

43
Sam Jones

Dans .NET Core 1.0, vous pouvez définir cela comme paramètre global dans votre fichier Startup.cs:

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));
        });
    }
21
Caleb

Nous pouvons ajouter ces deux lignes dans le constructeur de la classe DbContext pour désactiver la boucle d'auto-référencement, comme

public TestContext()
        : base("name=TestContext")
{
    this.Configuration.LazyLoadingEnabled = false;
    this.Configuration.ProxyCreationEnabled = false;
}
8
Sanjay Nishad

Pour sérialiser usin NEWTONSOFTJSON lorsque vous avez un problème de boucle, dans mon cas, je n'avais pas besoin de modifier global.asax ni d'apiconfig. Je viens d'utiliser JsonSerializesSettings en ignorant la gestion en boucle.

JsonSerializerSettings jss = new JsonSerializerSettings();
jss.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
var lst = db.shCards.Where(m => m.CardID == id).ToList();
string json = JsonConvert.SerializeObject(lst, jss);
8
Carlos Barini

Si vous utilisez .NET Core 2.0, mettez à jour votre section ConfigureServices dans Startup.cs.

https://docs.Microsoft.com/en-us/ef/core/querying/related-data#related-data-and-serialization

public void ConfigureServices(IServiceCollection services)
{
...

services.AddMvc()
    .AddJsonOptions(
        options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
    );

...
}
5
Dave Skender

Vous pouvez également appliquer un attribut à la propriété. L'attribut [JsonProperty( ReferenceLoopHandling = ... )] est bien adapté à cela.

Par exemple:

/// <summary>
/// Represents the exception information of an event
/// </summary>
public class ExceptionInfo
{
    // ...code omitted for brevity...

    /// <summary>
    /// An inner (nested) error.
    /// </summary>
    [JsonProperty( ReferenceLoopHandling = ReferenceLoopHandling.Ignore, IsReference = true )]
    public ExceptionInfo Inner { get; set; }

    // ...code omitted for brevity...    
}

J'espère que ça aide, Jaans

5
Jaans

Pour ignorer les références de boucle et ne pas les sérialiser globalement dans MVC 6, utilisez ce qui suit dans startup.cs:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().Configure<MvcOptions>(options =>
        {
            options.OutputFormatters.RemoveTypesOf<JsonOutputFormatter>();
            var jsonOutputFormatter = new JsonOutputFormatter();
            jsonOutputFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            options.OutputFormatters.Insert(0, jsonOutputFormatter);
        });
    }
4
GerardBeckerleg

Pour moi, je devais prendre un chemin différent. Au lieu d'essayer de réparer le sérialiseur JSON.Net, je devais rechercher le chargement paresseux sur mon contexte de données.

Je viens d'ajouter ceci à mon référentiel de base:

context.Configuration.ProxyCreationEnabled = false;

L'objet "context" est un paramètre de constructeur que j'utilise dans mon référentiel de base car j'utilise l'injection de dépendances. Vous pouvez modifier la propriété ProxyCreationEnabled à n'importe quel endroit où vous instanciez votre contexte de données.

http://techie-tid-bits.blogspot.com/2015/09/jsonnet-serializer-and-error-self.html

2
Xipooo

J'ai eu cette exception et ma solution de travail est facile et simple,

Ignorez la propriété référencée en y ajoutant l'attribut JsonIgnore:

[JsonIgnore]
public MyClass currentClass { get; set; }

Réinitialisez la propriété lorsque vous la désérialisez:

Source = JsonConvert.DeserializeObject<MyObject>(JsonTxt);
foreach (var item in Source)
        {
            Source.MyClass = item;
        }

using Newtonsoft.Json;

2
Mayer Spitzer

Utilisez ceci dans la classe WebApiConfig.cs:

var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
2
Anand Kumar

Équipe:

Cela fonctionne avec ASP.NET Core; Le défi de ce qui précède est de savoir comment définir le paramètre pour ignorer. En fonction de la configuration de votre application, cela peut être assez difficile. Voici ce qui a fonctionné pour moi.

Cela peut être placé dans votre section ConfigureServices (services IServiceCollection) vide publique.

services.AddMvc().AddJsonOptions(opt => 
        { 
      opt.SerializerSettings.ReferenceLoopHandling =
      Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        });
1
FlyingV

On a déjà parlé de l'ajout de [JsonIgnore] à la propriété virtuelle de la classe, par exemple:

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

Je partagerai également une autre option, [JsonProperty (NullValueHandling = NullValueHandling.Ignore)], qui omet la propriété de la sérialisation uniquement si elle est null:

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public virtual Project Project { get; set; }
1
Ali Raza

Mon problème résolu avec la configuration personnalisée JsonSerializerSettings:

services.AddMvc(
  // ...
               ).AddJsonOptions(opt =>
                 {
                opt.SerializerSettings.ReferenceLoopHandling =
                    Newtonsoft.Json.ReferenceLoopHandling.Serialize;
                opt.SerializerSettings.PreserveReferencesHandling =
                    Newtonsoft.Json.PreserveReferencesHandling.Objects;
                 });
0
AminGolmahalle

Il suffit de placer Configuration.ProxyCreationEnabled = false; dans le fichier de contexte; cela résoudra le problème.

public demEntities()
    : base("name=demEntities")
{
    Configuration.ProxyCreationEnabled = false;
}
0
fraka