JSON.NET-Fehler Selbstreferenzierende Schleife für Typ erkannt


494

Ich habe versucht, die POCO-Klasse zu serialisieren, die automatisch aus dem Entity Data Model .edmx generiert wurde, und als ich sie verwendet habe

JsonConvert.SerializeObject 

Ich habe folgenden Fehler erhalten:

Fehler Es tritt eine selbstreferenzierende Schleife für den Typ System.data.entity auf.

Wie löse ich dieses Problem?



Wenn Sie Linq und MVC verwenden: stackoverflow.com/a/38241856
aDDin

bei Verwendung von .NET Core 2: stackoverflow.com/a/48709134/4496145
Dave Skender

2
Dieser Fehler ist mir passiert, als ich das Ergebnis eines asyncMethodenaufrufs (a Task) serialisieren wollte und vergessen habe, der awaitAnweisung ein Präfix zu setzen .
Uwe Keim

Antworten:


485

Das war die beste Lösung https://code.msdn.microsoft.com/Loop-Reference-handling-in-caaffaf7

Fix 1: Zirkelverweis global ignorieren

(Ich habe dieses ausgewählt / ausprobiert, wie viele andere auch)

Der json.net-Serializer bietet die Möglichkeit, Zirkelverweise zu ignorieren. Fügen Sie den folgenden Code in die WebApiConfig.csDatei ein:

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

Durch die einfache Korrektur ignoriert der Serializer die Referenz, was zu einer Schleife führt. Es gibt jedoch Einschränkungen:

  • Die Daten verlieren die Schleifenreferenzinformationen
  • Der Fix gilt nur für JSON.net
  • Die Referenzstufe kann nicht gesteuert werden, wenn eine tiefe Referenzkette vorhanden ist

Wenn Sie diesen Fix in einem ASP.NET-Projekt ohne API verwenden möchten, können Sie die obige Zeile Global.asax.cshinzufügen, aber zuerst hinzufügen:

var config = GlobalConfiguration.Configuration;

Wenn Sie dies in einem .Net Core- Projekt verwenden möchten , können Sie Folgendes ändern Startup.cs:

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

Fix 2: Zirkelverweis global beibehalten

Dieser zweite Fix ähnelt dem ersten. Ändern Sie einfach den Code in:

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

Die Datenform wird nach dem Anwenden dieser Einstellung geändert.

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

Die $ id und $ ref behalten alle Referenzen bei und machen das Objektdiagramm flach, aber der Client-Code muss die Formänderung kennen, um die Daten zu verbrauchen, und dies gilt auch nur für den JSON.NET-Serializer.

Fix 3: Referenzattribute ignorieren und beibehalten

Mit diesem Fix werden Attribute für die Modellklasse dekoriert, um das Serialisierungsverhalten auf Modell- oder Eigenschaftsebene zu steuern. So ignorieren Sie die Eigenschaft:

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

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

JsonIgnore ist für JSON.NET und IgnoreDataMember ist für XmlDCSerializer. So behalten Sie die Referenz bei:

 // 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)]ist für JSON.NET und [DataContract(IsReference = true)]ist für XmlDCSerializer. Beachten Sie Folgendes: Nach dem Anwenden DataContractauf die Klasse müssen Sie DataMemberEigenschaften hinzufügen , die Sie serialisieren möchten.

Die Attribute können sowohl auf den JSON- als auch auf den XML-Serializer angewendet werden und bieten mehr Steuerelemente für die Modellklasse.


7
Fix 3 ist für mich gearbeitet. Entfernen Sie einfach die Attribute DataContract und DataMember und setzen Sie JsonObject (IsReference = true) auf DTOs. Und es funktioniert. Vielen Dank.
Maestro

1
versuchen Sie diese eine GlobalConfiguration.Configuration
Bishoy Hanna

1
Fix 3 hat den Vorteil, dass es auf Client-Code funktioniert, wo es keine GlobalConfiguration gibt
dumbledad

1
@BishoyHanna, können Sie Ihre Antwort so bearbeiten, dass sie von normalen ASP.NET-Anwendungen verwendet werden kann? Sie können meine vorgeschlagene Bearbeitung verwenden: stackoverflow.com/review/suggested-edits/17797683
NH.

2
Die Verwendung des [JsonIgnore]obigen Attributs hat bei mir funktioniert.
Nathan Beck

467

Verwenden Sie JsonSerializerSettings

  • ReferenceLoopHandling.Error(Standard) tritt ein Fehler auf, wenn eine Referenzschleife auftritt. Aus diesem Grund erhalten Sie eine Ausnahme.
  • ReferenceLoopHandling.Serialize ist nützlich, wenn Objekte verschachtelt, aber nicht unbegrenzt sind.
  • ReferenceLoopHandling.Ignore serialisiert ein Objekt nicht, wenn es ein untergeordnetes Objekt von sich selbst ist.

Beispiel:

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

Wenn Sie ein Objekt serialisieren müssen, das auf unbestimmte Zeit verschachtelt ist, können Sie PreserveObjectReferences verwenden , um eine StackOverflowException zu vermeiden.

Beispiel:

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

Wählen Sie aus, was für das zu serialisierende Objekt sinnvoll ist.

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


66
Beim Serialisieren einer Datentabelle ist der Fehler aufgetreten. Ich habe es benutzt, ReferenceLoopHandling = ReferenceLoopHandling.Ignoreum zu funktionieren

8
Wenn die Daten Referenzschleifen enthalten, führt die Verwendung von ReferenceLoopHandling.Serializedazu, dass der Serializer in eine Endlosschleife übergeht und den Stapel überläuft.
Brian Rogers

1
Richtig. Da es sich bei der Frage um ein EF-Modell handelt, ist dies ebenfalls ein berechtigtes Anliegen. Geändert, um alle verfügbaren Optionen anzugeben.
DalSoft

1
Ich habe den gleichen Fehler beim Versuch, ein Objekt zu serialisieren ... aber das Objekt hat keine anderen Referenzen als einen Aufzählungstyp ..
Marin

1
Für mich ist EF die Hauptursache für dieses Problem, da selbstreferenzierte Entitäten überall sind.
Teoman Shipahi

57

Das Update besteht darin, Schleifenreferenzen zu ignorieren und nicht zu serialisieren. Dieses Verhalten ist in angegeben JsonSerializerSettings.

SingleJsonConvert mit Überlastung:

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

Globale Einstellung mit Code Application_Start()in Global.asax.cs:

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

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


Warum setzen Sie das Format auf eingerückt, wenn Sie die globale Einstellung vornehmen?
Murphybro2

Absolut das, was wir brauchten, um dieses Problem zu lösen (entdeckt während einer Bereitstellung)! Du da Mann .... danke, dass du uns Zeit gespart hast !!
Ryan Eastabrook

Ich habe mein Problem gelöst, indem ich "JsonConvert.DefaultSettings" = () => neue JsonSerializerSettings {....} in der Klasse "Startup.cs"
Beldi Anouar

45

Der einfachste Weg, dies zu tun, besteht darin, Json.NET von Nuget aus zu installieren und das [JsonIgnore]Attribut der virtuellen Eigenschaft in der Klasse hinzuzufügen , zum Beispiel:

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

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

Obwohl ich heutzutage ein Modell nur mit den Eigenschaften erstelle, die ich durchlaufen möchte, damit es leichter ist, keine unerwünschten Sammlungen enthält und ich meine Änderungen nicht verliere, wenn ich die generierten Dateien neu erstelle ...


3
Beste Antwort mit Newton JSON
Aizen

21

In .NET Core 1.0 können Sie dies als globale Einstellung in Ihrer Startup.cs-Datei festlegen:

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

In diesem Fall erhalte ich jedoch keine Ausnahme, wenn ich wissen möchte, dass diese Eigenschaft ignoriert wird.
Mayer Spitzer

10

Wenn Sie .NET Core 2.x verwenden, aktualisieren Sie den Abschnitt ConfigureServices in 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
        );

    ...
    }

Wenn Sie .NET Core 3.x ohne MVC verwenden, ist dies:

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

Diese Behandlung von Referenzschleifen ist fast obligatorisch, wenn Sie Entity Framework und das Entwurfsmuster "Datenbank zuerst" verwenden.


2
Was ist, wenn ich nicht benutze services.AddMvc()?
Prisar

2
Ist das eine schlechte Praxis?
Renan Coelho

Auf den ersten Blick könnte man denken, dass dies eine schlechte Praxis ist, da sie das "absichtliche Design" zur Vermeidung des alten "Endlosschleifen" -Problems außer Kraft setzen könnte. Wenn Sie jedoch über Ihre Anwendungsfälle für Klassen nachdenken, müssen diese möglicherweise aufeinander verweisen. Beispielsweise möchten Sie möglicherweise sowohl auf Bäume> Früchte als auch auf Früchte> Bäume zugreifen.
Dave Skender

Wenn Sie ein Datenbank-First-Entwurfsmuster mit etwas wie Entity Framework verwenden, werden diese zyklischen Referenzen automatisch erstellt, je nachdem, wie Sie Ihre Fremdschlüssel in Ihrer Datenbank einrichten. Wenn Sie dies tun, müssen Sie diese Einstellung also so ziemlich verwenden Reverse Engineering Ihrer Klassen.
Dave Skender

9

Um in NEWTONSOFTJSON zu serialisieren, wenn Sie ein Schleifenproblem haben, musste ich in meinem Fall weder global.asax noch apiconfig ändern. Ich benutze nur JsonSerializesSettings und ignoriere die Schleifenbehandlung.

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

1
Wenn jemand anderes hierher gekommen ist, um einen Einzeiler in das Überwachungsfenster zu bringen, damit der Text durchsucht werden kann:Newtonsoft.Json.JsonConvert.SerializeObject(objToSerialize, new Newtonsoft.Json.JsonSerializerSettings() {ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore});
Graham,

8

Wir können diese beiden Zeilen in den DbContext-Klassenkonstruktor einfügen, um die selbstreferenzierende Schleife zu deaktivieren

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

Dies ist eine der einfachsten und funktioniert wie ein Zauber . Abgestimmt, vielen Dank ...
Murat Yıldız

Wie ich in der anderen Frage geschrieben habe: Ich mag diese Art von Antworten nicht, weil Sie eine Funktion von EF6 deaktivieren, die standardmäßig aktiviert ist, und dieser Code möglicherweise andere Teile des Programms beschädigt. Sie sollten erklären, was dies bewirkt und welche Auswirkungen es hat.
El Mac

@ElMac Sie haben Recht, aber wenn wir diese Funktion nicht benötigen, warum können wir diese Lösung dann nicht verwenden?
Sanjay Nishad

@ SanjayNishad Es macht mir nichts aus, wenn Sie die Funktion nicht benötigen. Es geht nur um die Benutzer, die nicht wissen, was sie deaktivieren.
El Mac

6

Sie können der Eigenschaft auch ein Attribut zuweisen. Das [JsonProperty( ReferenceLoopHandling = ... )]Attribut ist dafür gut geeignet.

Zum Beispiel:

/// <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...    
}

Hoffe das hilft, Jaans


4

Verwenden Sie in startup.cs Folgendes, um Schleifenreferenzen zu ignorieren und sie in MVC 6 nicht global zu serialisieren:

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

2

Verwenden Sie dies in der WebApiConfig.csKlasse:

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

2

Für mich musste ich einen anderen Weg gehen. Anstatt zu versuchen, den JSON.Net-Serializer zu reparieren, musste ich das Lazy Loading in meinem Datenkontext durchführen.

Ich habe dies gerade zu meinem Basis-Repository hinzugefügt:

context.Configuration.ProxyCreationEnabled = false;

Das "Kontext" -Objekt ist ein Konstruktorparameter, den ich in meinem Basis-Repository verwende, da ich die Abhängigkeitsinjektion verwende. Sie können die ProxyCreationEnabled-Eigenschaft überall dort ändern, wo Sie stattdessen Ihren Datenkontext instanziieren.

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


2

Ich hatte diese Ausnahme und meine Arbeitslösung ist einfach und unkompliziert.

Ignorieren Sie die Referenced-Eigenschaft, indem Sie das JsonIgnore-Attribut hinzufügen:

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

Setzen Sie die Eigenschaft zurück, wenn Sie sie deserialisieren:

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

mit Newtonsoft.Json;


Das ist die Magie, die ich brauche. Löse es[JsonIgnore]
saviour123

2

Mannschaft:

Dies funktioniert mit ASP.NET Core. Die Herausforderung für das oben Gesagte besteht darin, wie Sie die Einstellung so einstellen, dass sie ignoriert wird. Je nachdem, wie Sie Ihre Anwendung einrichten, kann dies eine große Herausforderung sein. Hier ist, was für mich funktioniert hat.

Dies kann in Ihrem öffentlichen ungültigen Abschnitt ConfigureServices (IServiceCollection-Dienste) platziert werden.

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

2

Die Leute haben bereits darüber gesprochen, dass [JsonIgnore] zur virtuellen Eigenschaft in der Klasse hinzugefügt wird, zum Beispiel:

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

Ich werde auch eine andere Option freigeben, [JsonProperty (NullValueHandling = NullValueHandling.Ignore)], bei der die Eigenschaft bei der Serialisierung nur dann weggelassen wird, wenn sie null ist:

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


0

Einfach Configuration.ProxyCreationEnabled = false;in die Kontextdatei einfügen; Dies wird das Problem lösen.

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

0

Mein mit der benutzerdefinierten Konfiguration gelöstes Problem JsonSerializerSettings:

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

0

Stellen Sie außerdem sicher, dass Sie in Ihrer Methode await und async verwenden. Sie können diesen Fehler erhalten, wenn Ihr Objekt nicht ordnungsgemäß serialisiert ist.


0

Ich hatte das gleiche Problem und habe versucht, mit JsonSetting den selbstreferenzierenden Fehler zu ignorieren, bis ich eine Klasse bekam, die sehr tief auf sich selbst verweist und mein Punktnetzprozess vom Json-Schreibwert abhängt.

Mein Problem

    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; }

}

Sie können das Problem in der Benutzerklasse sehen, die auf CompanyUser verweist Klasse , die sich selbst referenziert.

Jetzt rufe ich die GetAll-Methode auf, die alle relationalen Eigenschaften enthält.

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

In dieser Phase hängt mein DotNetCore-Prozess an der Ausführung von JsonResult, dem Schreiben von Werten ... und kommt nie. In meiner Startup.cs habe ich bereits die JsonOption festgelegt. Aus irgendeinem Grund enthält EFCore verschachtelte Eigenschaften, die ich Ef nicht geben soll.

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

erwartetes Verhalten sollte dies sein

Hey EfCore, können Sie bitte auch "CompanyUsers" -Daten in meine Company-Klasse aufnehmen, damit ich leicht auf die Daten zugreifen kann?

dann

Hey EfCore, können Sie bitte auch die "CompanyUsers.User" -Daten angeben, damit ich einfach auf die Daten wie diese Company.CompanyUsers.First () zugreifen kann. User.DisplayName

Zu diesem Zeitpunkt sollte ich nur dieses "Company.CompanyUsers.First (). User.DisplayName" erhalten und es sollte mir nicht Company.CompanyUsers.First (). User.CompanyUsers geben, das das Problem der Selbstreferenzierung verursacht. Technisch sollte es mir nicht User.CompanyUsers geben, da CompanyUsers eine Navigationseigenschaft ist. Aber EfCore wird sehr aufgeregt und gibt mir User.CompanyUsers .

Daher habe ich beschlossen, eine Erweiterungsmethode für die Eigenschaft zu schreiben, die vom Objekt ausgeschlossen werden soll (sie schließt eigentlich nicht aus, sondern setzt die Eigenschaft nur auf null). Nicht nur, dass es auch mit Array-Eigenschaften funktioniert. Unten ist der Code, den ich auch für andere Benutzer exportieren werde (nicht sicher, ob dies überhaupt jemandem hilft). Der Grund ist einfach, weil ich zu faul bin, um zu schreiben. Wählen Sie (n => new {n.p1, n.p2});Ich möchte einfach keine select-Anweisung schreiben, um nur 1 Eigenschaft auszuschließen!

Dies ist nicht der beste Code (ich werde ihn irgendwann aktualisieren), da ich es eilig habe und obwohl dies jemandem helfen könnte, der das Objekt auch mit Arrays ausschließen (null setzen) möchte.

    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();
    }
}

Mit der obigen Erweiterungsklasse können Sie die Eigenschaft auf null setzen, um zu vermeiden, dass die selbstreferenzierende Schleife auch Arrays enthält.

Ausdrucksgenerator

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

Verwendung:

Modellklassen

    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; }
}

Dummy-Daten

           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" } } },

                }
            }
        };

Fälle:

Fall 1: Schließen Sie nur Eigenschaften ohne Array aus

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

Fall 2: Eigenschaft mit 1 Array ausschließen

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

Fall 3: Eigenschaft mit 2 verschachtelten Arrays ausschließen

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

Fall 4: EF GetAll-Abfrage mit Includes

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

Sie haben festgestellt, dass die Explode () -Methode auch eine Erweiterungsmethode ist, nur damit unser Ausdrucksgenerator die Eigenschaft von der Array-Eigenschaft abruft. Wenn eine Array-Eigenschaft vorhanden ist, verwenden Sie .Explode (). YourPropertyToExclude oder .Explode (). Property1.MyArrayProperty.Explode (). MyStupidProperty . Der obige Code hilft mir, die Selbstreferenzierung so tief wie möglich zu vermeiden. Jetzt kann ich GetAll verwenden und die Eigenschaft ausschließen, die ich nicht will!

Vielen Dank für das Lesen dieses großen Beitrags!



-1

C # -Code:

            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);

Dies ist im Wesentlichen die gleiche Anleitung wie in der hoch bewerteten Antwort von @ DalSoft vor acht Jahren, jedoch mit weitaus weniger Erklärung.
Jeremy Caney

Hoffe, es wird das Problem lösen, aber bitte fügen Sie eine Erklärung Ihres Codes hinzu, damit der Benutzer ein perfektes Verständnis dafür erhält, was er wirklich will.
Jaimil Patel

-2

Ich mochte die Lösung, die es macht, Application_Start()wie in der Antwort hier

Anscheinend konnte ich mit der Konfiguration in meiner Funktion wie in DalSofts Antwort nicht auf die json-Objekte in JavaScript zugreifen, da das zurückgegebene Objekt "\ n \ r" über das gesamte (Schlüssel, Wert) des Objekts hatte.

Was auch immer funktioniert, es ist großartig (da unterschiedliche Ansätze in unterschiedlichen Szenarien funktionieren, basierend auf den gestellten Kommentaren und Fragen), obwohl eine Standardmethode mit einer guten Dokumentation, die den Ansatz unterstützt, vorzuziehen wäre.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.