2014-10-17 13 views
13

Ich habe ein Problem beim Abrufen einiger ordnungsgemäß serialisierter Daten von meinem ASP.NET-Web-API-Controller mit Newtonsoft.Json.Wie "really" Sie zirkuläre Referenzierungsobjekte mit Newtonsoft.Json serialisieren?

Hier ist, was ich denke geht - bitte korrigieren Sie mich, wenn ich falsch liege. Unter bestimmten Umständen (insbesondere wenn keine Zirkelverweise in den Daten vorhanden sind) funktioniert alles so, wie Sie es erwarten würden - eine Liste der aufgefüllten Objekte wird serialisiert und zurückgegeben. Wenn ich Daten einführe, die einen Zirkelverweis im Modell verursachen (unten beschrieben und sogar mit PreserveReferencesHandling.Objects), werden nur die Elemente der Liste, die zum ersten Objekt mit einem Zirkelverweis führen, so serialisiert, dass der Klient "damit arbeiten kann ". Die "Elemente, die zu" führen, können irgendwelche der Elemente in den Daten sein, wenn sie anders angeordnet sind, bevor sie an den Serialisierer gesendet werden, aber mindestens einer wird auf eine Weise serialisiert, mit der der Client "arbeiten kann". Die leeren Objekte werden als Newtonsoft-Referenzen serialisiert ({$ref:X}).

Zum Beispiel, wenn ich ein EF-Modell mit Navigationseigenschaften komplett haben, die wie folgt aussieht:

Model

In meinem global.asax:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; 
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects; 

Hier ist die grundlegende Abfrage I‘ m mit Entity Framework (lazy-loading ist aus, so dass hier keine Proxy-Klassen vorhanden sind):

[HttpGet] 
[Route("starting")] 
public IEnumerable<Balance> GetStartingBalances() 
{ 
    using (MyContext db = new MyContext()) 
    { 
     var data = db.Balances 
     .Include(x => x.Source) 
     .Include(x => x.Place) 
     .ToList() 
     return data; 
    } 
} 

So weit so gut, data ist bestückt.

Wenn es keine Zirkelverweise gibt, ist das Leben großartig. Sobald jedoch 2 Balance Entities mit dem gleichen Source oder Place vorliegen, werden durch die Serialisierung die späteren Objekte der obersten Liste, die ich in Newtonsoft-Referenzen zurückgebe, anstelle ihrer vollwertigen Objekte in die späteren Objekte umgewandelt, weil sie bereits vorhanden waren in der Balances Eigenschaft des Source oder Place Objekts serialisiert (e):

[{"$id":"1","BalanceID":4,"SourceID":2,"PlaceID":2 ...Omitted for clarity...},{"$ref":"4"}] 

das Problem dabei ist, dass der Kunde nicht weiß, was mit {$ref:4} zu tun, auch wenn wir Menschen verstehen, was los ist. In meinem Fall bedeutet dies, dass ich AngularJS nicht zu ng-repeat über meine gesamte Liste von Salden mit diesem JSON verwenden kann, da sie nicht alle Balance Objekte mit einer Balance-Eigenschaft sind, die gebunden werden soll. Ich bin mir sicher, dass es viele andere Anwendungsfälle gibt, die das gleiche Problem haben.

Ich kann die json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects nicht ausschalten, weil viele andere Dinge brechen würden (was in 100 anderen Fragen hier und anderswo gut dokumentiert ist).

Gibt es eine bessere Abhilfe für diesen außer in dem Web-API-Controller durch die Einheiten gehen und

Balance.Source.Balances = null; 

auf alle Navigationseigenschaften machen die zirkulären Verweise zu brechen? Weil das auch nicht richtig scheint.

Antwort

15

Ja, mit PreserveReferencesHandling.Objects ist wirklich die beste Möglichkeit, ein Objektdiagramm mit Zirkelverweise zu serialisieren, weil es das kompakteste JSON erzeugt und tatsächlich die Referenzstruktur des Objektgraphen beibehält. Das heißt, wenn Sie das JSON wieder zu Objekten deserialisieren (mit einer Bibliothek, die die Notation $id und $ref versteht), verweist jede Referenz auf ein bestimmtes Objekt auf die gleiche Instanz des Objekts und nicht auf mehrere Instanzen mit denselben Daten.

In Ihrem Fall ist das Problem, dass Ihr Client-Parser die von Json.Net erzeugte $id- und $ref-Notation nicht versteht, so dass die Referenzen nicht aufgelöst werden. Dies kann behoben werden, indem eine JavaScript-Methode verwendet wird, um die Objektverweise nach dem Deserialisieren des JSON zu rekonstruieren. Beispiele siehe here und here.

Eine weitere Möglichkeit, die je nach Situation funktionieren könnte, ist ReferenceLoopHandling zu Ignore zu setzen, wenn Einstellung PreserveReferencesHandling auf Objects statt Serialisierung. Dies ist jedoch keine perfekte Lösung. Eine detaillierte Erläuterung der Unterschiede zwischen der Verwendung von ReferenceLoopHandling.Ignore und PreserveReferencesHandling.Objects finden Sie unter this question.

Verwandte Themen