Erreur JSON.NET Boucle de référencement automatique détectée pour le type

495

J'ai essayé de sérialiser la classe POCO qui a été générée automatiquement à partir du modèle de données d'entité .edmx et quand j'ai utilisé

JsonConvert.SerializeObject 

J'ai eu l'erreur suivante:

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

Comment résoudre ce problème?

NevenHuynh
la source
lorsque vous utilisez Linq et MVC: stackoverflow.com/a/38241856
aDDin
lors de l'utilisation de .NET Core 2: stackoverflow.com/a/48709134/4496145
Dave Skender
2
Cette erreur m'est arrivée lorsque j'ai voulu sérialiser le résultat d'un asyncappel de méthode (a Task) et j'ai oublié de préfixer l' awaitinstruction.
Uwe Keim

Réponses:

485

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

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

(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. Mettez le code suivant dans le WebApiConfig.csfichier:

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

Le correctif simple obligera le sérialiseur à ignorer 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 ne s'applique qu'à 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 l'utiliser dans le projet .Net Core , vous pouvez modifier Startup.cscomme:

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

Correctif 2: conservation globale des références circulaires

Ce deuxième correctif est similaire au premier. Changez simplement 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 conserve toutes les références et rend le niveau du graphique objet plat, mais le code client a besoin de connaître le changement de forme pour consommer les données et il s'applique uniquement au sérialiseur JSON.NET.

Correctif 3: ignorer et conserver les attributs de référence

Cette correction consiste à décorer les attributs de la classe de modèle pour contrôler le comportement de 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)]est pour JSON.NET et [DataContract(IsReference = true)]est pour XmlDCSerializer. Notez que: après avoir appliqué DataContractsur la classe, vous devez ajouter DataMemberaux propriétés que vous souhaitez sérialiser.

Les attributs peuvent être appliqués à la fois au sérialiseur json et xml et offrent plus de contrôles sur la classe de modèle.

Bishoy Hanna
la source
7
Le correctif 3 a fonctionné pour moi. Supprimez simplement les attributs DataContract et DataMember et placez JsonObject (IsReference = true) sur les DTO. Et il fonctionne. Merci.
maestro
1
essayez celui-ci GlobalConfiguration.Configuration
Bishoy Hanna
1
Le correctif 3 a l'avantage de fonctionner sur le code client où il n'y a pas de GlobalConfiguration
dumbledad
1
@BishoyHanna, pouvez-vous modifier votre réponse pour qu'elle puisse être utilisée à partir d'applications ASP.NET normales? Vous pouvez utiliser ma modification suggérée: stackoverflow.com/review/suggested-edits/17797683
NH.
2
L'utilisation de [JsonIgnore]l'attribut ci-dessus a fonctionné pour moi.
Nathan Beck
467

Utiliser JsonSerializerSettings

  • ReferenceLoopHandling.Error(par défaut) affichera 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 est 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 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/

DalSoft
la source
66
J'ai rencontré l'erreur lors de la sérialisation d'une table de données. Je l'ai utilisé ReferenceLoopHandling = ReferenceLoopHandling.Ignorepour fonctionner
8
S'il y a des boucles de référence dans les données, l'utilisation ReferenceLoopHandling.Serializeentraînera le sérialiseur à entrer dans une boucle récursive infinie et à déborder la pile.
Brian Rogers
1
Correct. Étant donné que la question concerne un modèle EF, il s'agit également d'une préoccupation valable. Modifié pour donner toutes les options disponibles.
DalSoft du
1
J'ai rencontré cette même erreur en essayant de sérialiser un objet ... cependant, l'objet n'a pas de références autres qu'un type enum.
Marin
1
pour moi, EF est la principale cause de ce problème car les entités auto-référencées sont partout.
Teoman shipahi
58

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

SimpleJsonConvert avec surcharge:

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

Paramètre global avec le code 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

smockle
la source
Pourquoi définissez-vous le format en retrait lorsque vous effectuez le réglage global?
Murphybro2
Absolument ce dont nous avions besoin pour résoudre ce problème (découvert lors d'un déploiement)! You da man .... merci de nous avoir fait gagner du temps !!
Ryan Eastabrook
J'ai résolu mes problèmes en ajoutant "JsonConvert.DefaultSettings" = () => new JsonSerializerSettings {....} dans la classe "Startup.cs"
Beldi Anouar
45

La façon la plus simple de procéder consiste à installer Json.NET à partir de nuget et à ajouter l' [JsonIgnore]attribut à 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 léger, n'inclut pas les collections indésirables et je ne perds pas mes modifications lorsque je reconstruis les fichiers générés ...

Sam Jones
la source
3
Meilleure réponse avec Newton JSON
Aizen
21

Dans .NET Core 1.0, vous pouvez définir cela en tant que 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));
        });
    }
Caleb
la source
Mais dans ce cas, si je veux savoir que cette propriété est ignorée, je n'obtiendrai alors aucune exception.
Mayer Spitzer
10

Si vous utilisez .NET Core 2.x, 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
        );

    ...
    }

Si vous utilisez .NET Core 3.x sans MVC, ce serait:

services.AddControllers()
  .AddNewtonsoftJson(options =>
      options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
   );

Cette gestion de boucle de référence est presque obligatoire si vous utilisez Entity Framework et un modèle de conception basé sur la base de données.

Dave Skender
la source
2
et si je n'utilise pas services.AddMvc()?
prisar
2
est-ce une mauvaise pratique?
Renan Coelho
À première vue, vous pourriez penser que c'est une mauvaise pratique car cela pourrait remplacer la «conception intentionnelle» d'éviter l'ancien problème de «boucle infinie». Cependant, si vous pensez à vos cas d'utilisation pour les classes, vous pourriez avoir besoin qu'ils se réfèrent les uns aux autres. Par exemple, vous souhaiterez peut-être accéder à la fois à Arbres> Fruits et également à Fruits> Arbres.
Dave Skender du
De plus, si vous utilisez un modèle de conception basé sur la base de données avec quelque chose comme Entity Framework, selon la façon dont vous configurez vos clés étrangères dans votre base de données, il créera automatiquement ces références cycliques, vous devrez donc à peu près utiliser ce paramètre si vous réingénierie de vos cours.
Dave Skender du
9

Pour sérialiser usin NEWTONSOFTJSON lorsque vous avez un problème de boucle, dans mon cas, je n'ai pas eu besoin de modifier global.asax ou apiconfig. J'utilise simplement JsonSerializesSettings en ignorant la gestion des boucles.

JsonSerializerSettings jss = new JsonSerializerSettings();
jss.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
var lst = db.shCards.Where(m => m.CardID == id).ToList();
string json = JsonConvert.SerializeObject(lst, jss);
Carlos Barini
la source
1
Si quelqu'un d'autre est venu ici pour qu'un liner passe dans la fenêtre de surveillance, il est donc possible de rechercher du texte:Newtonsoft.Json.JsonConvert.SerializeObject(objToSerialize, new Newtonsoft.Json.JsonSerializerSettings() {ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore});
Graham
8

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

public TestContext()
        : base("name=TestContext")
{
    this.Configuration.LazyLoadingEnabled = false;
    this.Configuration.ProxyCreationEnabled = false;
}
Sanjay Nishad
la source
C'est l'un des plus simples et fonctionne comme un charme . Voté, merci beaucoup ...
Murat Yıldız
Comme je l'ai écrit dans l'autre question: je n'aime pas ce genre de réponses parce que vous désactivez une fonctionnalité d'EF6 qui est activée par défaut et ce morceau de code peut casser d'autres parties du programme. Vous devez expliquer ce que cela fait et quel genre de répercussions cela a.
El Mac
@ElMac vous avez raison, mais si nous n'avons pas besoin de cette fonctionnalité, alors pourquoi ne pas utiliser cette solution?
Sanjay Nishad
@SanjayNishad Cela ne me dérange pas si vous n'avez pas besoin de la fonctionnalité. Il s'agit uniquement des utilisateurs qui ne savent pas ce qu'ils désactivent.
El Mac
6

Vous pouvez également appliquer un attribut à la propriété. le[JsonProperty( ReferenceLoopHandling = ... )] attribut 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

Jaans
la source
4

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);
        });
    }
GerardBeckerleg
la source
2

Utilisez ceci en WebApiConfig.csclasse:

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

Pour moi, je devais emprunter une autre voie. Au lieu d'essayer de corriger le sérialiseur JSON.Net, j'ai dû aller après le chargement paresseux sur mon contexte de données.

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

context.Configuration.ProxyCreationEnabled = false;

L'objet "context" est un paramètre 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 partout où vous instanciez votre datacontext à la place.

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

Xipooo
la source
2

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

Ignorez la propriété référencée en lui 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;

Mayer Spitzer
la source
C'est la magie dont j'ai besoin. Résolvez-le[JsonIgnore]
saviour123
2

É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 à ignorer». Selon la façon dont vous configurez votre application, cela peut être assez difficile. Voici ce qui a fonctionné pour moi.

Cela peut être placé dans votre section ConfigureServices void public (services IServiceCollection).

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

Les gens ont 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 nulle:

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

Pour .NET Core 3.0, mettez à jour la classe Startup.cs comme indiqué ci-dessous.

public void ConfigureServices(IServiceCollection services)
{
...

services.AddControllers()
    .AddNewtonsoftJson(
        options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
    );

...
}

Voir: https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-core-3-0-preview-5/

Jimmy
la source
0

Placez simplement Configuration.ProxyCreationEnabled = false;à l'intérieur du fichier de contexte; cela résoudra le problème.

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

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;
                 });
AminGolmahalle
la source
0

Assurez-vous également d'utiliser wait et async dans votre méthode. Vous pouvez obtenir cette erreur si votre objet n'est pas sérialisé correctement.

maxspan
la source
0

J'étais confronté au même problème et j'ai essayé d'utiliser JsonSetting pour ignorer l'erreur d'auto-référencement, son travail un peu jusqu'à ce que j'obtienne une classe qui s'auto-référençant très profondément et mon processus dot-net se bloque sur la valeur d'écriture Json.

Mon problème

    public partial class Company : BaseModel
{
    public Company()
    {
        CompanyUsers = new HashSet<CompanyUser>();
    }

    public string Name { get; set; }

    public virtual ICollection<CompanyUser> CompanyUsers { get; set; }
}

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 partial class User : BaseModel
{
    public User()
    {
        CompanyUsers = new HashSet<CompanyUser>();
    }

    public string DisplayName { get; set; }
    public virtual ICollection<CompanyUser> CompanyUsers { get; set; }

}

Vous pouvez voir le problème dans la classe Utilisateur qui fait référence à CompanyUser classe qui est une auto-référence.

Maintenant, j'appelle la méthode GetAll qui inclut toutes les propriétés relationnelles.

cs.GetAll("CompanyUsers", "CompanyUsers.User");

À ce stade, mon processus DotNetCore dépend de l' exécution de JsonResult, de l'écriture de valeur ... et ne vient jamais. Dans mon Startup.cs, j'ai déjà défini le JsonOption. Pour une raison quelconque, EFCore inclut une propriété imbriquée que je ne demande pas à Ef de donner.

    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;

le comportement attendu devrait être ceci

Hey EfCore, pouvez-vous également inclure les données "CompanyUsers" dans ma classe Company afin que je puisse facilement accéder aux données.

puis

Hey EfCore pouvez-vous également inclure les données "CompanyUsers.User" afin que je puisse facilement accéder aux données comme celle-ci Company.CompanyUsers.First (). User.DisplayName

à ce stade, je ne devrais obtenir que "Company.CompanyUsers.First (). User.DisplayName" et il ne devrait pas me donner Company.CompanyUsers.First (). User.CompanyUsers qui est à l'origine du problème d'auto-référencement; Techniquement, cela ne devrait pas me donner User.CompanyUsers car CompanyUsers est une propriété de navigation. Mais, EfCore devient très excité et me donne User.CompanyUsers .

J'ai donc décidé d'écrire une méthode d'extension pour que la propriété soit exclue de l'objet (elle n'exclut pas en fait, elle définit simplement la propriété sur null). Non seulement cela fonctionnera également sur les propriétés du tableau. ci-dessous est le code que je vais également exporter le paquet nuget pour les autres utilisateurs (je ne sais pas si cela aide même quelqu'un). La raison est simple car je suis trop paresseux pour écrire .Select (n => new {n.p1, n.p2});Je ne veux tout simplement pas écrire l'instruction select pour exclure seulement 1 propriété!

Ce n'est pas le meilleur code (je mettrai à jour à un moment donné) comme je l'ai écrit à la hâte et bien que cela puisse aussi aider quelqu'un qui veut exclure (définir null) dans l'objet avec des tableaux.

    public static class PropertyExtensions
{
    public static void Exclude<T>(this T obj, Expression<Func<T, object>> expression)
    {
        var visitor = new PropertyVisitor<T>();
        visitor.Visit(expression.Body);
        visitor.Path.Reverse();
        List<MemberInfo> paths = visitor.Path;
        Action<List<MemberInfo>, object> act = null;

        int recursiveLevel = 0;
        act = (List<MemberInfo> vPath, object vObj) =>
        {

            // set last propert to null thats what we want to avoid the self-referencing error.
            if (recursiveLevel == vPath.Count - 1)
            {
                if (vObj == null) throw new ArgumentNullException("Object cannot be null");

                vObj.GetType().GetMethod($"set_{vPath.ElementAt(recursiveLevel).Name}").Invoke(vObj, new object[] { null });
                return;
            }

            var pi = vObj.GetType().GetProperty(vPath.ElementAt(recursiveLevel).Name);
            if (pi == null) return;
            var pv = pi.GetValue(vObj, null);
            if (pi.PropertyType.IsArray || pi.PropertyType.Name.Contains("HashSet`1") || pi.PropertyType.Name.Contains("ICollection`1"))
            {
                var ele = (IEnumerator)pv.GetType().GetMethod("GetEnumerator").Invoke(pv, null);

                while (ele.MoveNext())
                {
                    recursiveLevel++;
                    var arrItem = ele.Current;

                    act(vPath, arrItem);

                    recursiveLevel--;
                }

                if (recursiveLevel != 0) recursiveLevel--;
                return;
            }
            else
            {
                recursiveLevel++;
                act(vPath, pv);
            }

            if (recursiveLevel != 0) recursiveLevel--;

        };

        // check if the root level propert is array
        if (obj.GetType().IsArray)
        {
            var ele = (IEnumerator)obj.GetType().GetMethod("GetEnumerator").Invoke(obj, null);
            while (ele.MoveNext())
            {
                recursiveLevel = 0;
                var arrItem = ele.Current;

                act(paths, arrItem);
            }
        }
        else
        {
            recursiveLevel = 0;
            act(paths, obj);
        }

    }

    public static T Explode<T>(this T[] obj)
    {
        return obj.FirstOrDefault();
    }

    public static T Explode<T>(this ICollection<T> obj)
    {
        return obj.FirstOrDefault();
    }
}

La classe d'extension ci-dessus vous donnera la possibilité de définir la propriété sur null pour éviter la boucle d'auto-référencement, même les tableaux.

Générateur d'expression

    internal class PropertyVisitor<T> : ExpressionVisitor
{
    public readonly List<MemberInfo> Path = new List<MemberInfo>();

    public Expression Modify(Expression expression)
    {
        return Visit(expression);
    }


    protected override Expression VisitMember(MemberExpression node)
    {
        if (!(node.Member is PropertyInfo))
        {
            throw new ArgumentException("The path can only contain properties", nameof(node));
        }

        Path.Add(node.Member);
        return  base.VisitMember(node);
    }
}

Coutumes:

Classes de modèles

    public class Person
{
    public string Name { get; set; }
    public Address AddressDetail { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public Country CountryDetail { get; set; }
    public Country[] CountryDetail2 { get; set; }
}

public class Country
{
    public string CountryName { get; set; }
    public Person[] CountryDetail { get; set; }
}

Données factices

           var p = new Person
        {
            Name = "Adeel Rizvi",
            AddressDetail = new Address
            {
                Street = "Sydney",
                CountryDetail = new Country
                {
                    CountryName = "AU"
                }
            }
        };

        var p1 = new Person
        {
            Name = "Adeel Rizvi",
            AddressDetail = new Address
            {
                Street = "Sydney",
                CountryDetail2 = new Country[]
                {
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A1" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A2" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A3" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A4" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A5" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A6" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A7" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A8" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A9" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A1" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A2" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A3" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A4" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A5" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A6" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A7" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A8" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
                    new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A9" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },

                }
            }
        };

Cas:

Cas 1: exclure uniquement la propriété sans tableau

p.Exclude(n => n.AddressDetail.CountryDetail.CountryName);

Cas 2: exclure une propriété avec 1 tableau

p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryName);

Cas 3: exclure une propriété avec 2 tableaux imbriqués

p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryDetail.Explode().Name);

Cas 4: EF GetAll Query avec comprend

var query = cs.GetAll("CompanyUsers", "CompanyUsers.User").ToArray();
query.Exclude(n => n.Explode().CompanyUsers.Explode().User.CompanyUsers);
return query;

Vous avez remarqué que la méthode Explode () est également une méthode d'extension juste pour notre générateur d'expression pour obtenir la propriété de la propriété du tableau. Chaque fois qu'il existe une propriété de tableau, utilisez .Explode (). YourPropertyToExclude ou .Explode (). Property1.MyArrayProperty.Explode (). MyStupidProperty . Le code ci-dessus m'aide à éviter l'auto-référencement aussi profond que profond que je veux. Maintenant, je peux utiliser GetAll et exclure la propriété dont je ne veux pas!

Merci d'avoir lu ce gros post!

Adeel Rizvi
la source
-1

Pour ne pas boucler cela a fonctionné pour moi-
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,

J'ai résolu tout ici - sérialisation des enfants Entity Framework avec .Net Core 2 WebAPI https://gist.github.com/Kaidanov/f9ad0d79238494432f32b8407942c606

Appréciera toutes les remarques. peut-être que quelqu'un peut l'utiliser un jour.

Tzvi Gregory Kaidanov
la source
-1

Code C #:

            var jsonSerializerSettings = new JsonSerializerSettings
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
            };

            var jsonString = JsonConvert.SerializeObject(object2Serialize, jsonSerializerSettings);

            var filePath = @"E:\json.json";

            File.WriteAllText(filePath, jsonString);
Lilach Rayzin
la source
Il s'agit essentiellement des mêmes conseils que ceux proposés dans la réponse très appréciée de @ DalSoft il y a huit ans, mais avec beaucoup moins d'explications.
Jeremy Caney
J'espère que cela résoudra le problème, mais veuillez ajouter une explication de votre code afin que l'utilisateur comprenne parfaitement ce qu'il veut vraiment.
Jaimil Patel
-2

J'ai aimé la solution qui le fait Application_Start()comme dans la réponse ici

Apparemment, je ne pouvais pas accéder aux objets json en JavaScript en utilisant la configuration dans ma fonction comme dans la réponse de DalSoft car l'objet retourné avait "\ n \ r" partout dans la (clé, val) de l'objet.

Quoi qu'il en soit, tout ce qui fonctionne est excellent (car différentes approches fonctionnent dans différents scénarios en fonction des commentaires et des questions posées), bien qu'une méthode standard de le faire serait préférable avec une bonne documentation à l'appui de l'approche.

rey_coder
la source