2014-05-04 7 views
42

Ich betrachte eine WebAPI Anwendungsbeispiel, das diese codiert wurde:Was ist der Unterschied zwischen PreserveReferencesHandling und ReferenceLoopHandling in Json.Net?

json.SerializerSettings.PreserveReferencesHandling 
    = Newtonsoft.Json.PreserveReferencesHandling.Objects; 

und eine andere mit diesem codiert:

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

Weder erklären, warum jedes gewählt wird. Ich bin sehr neu in WebAPI, also kann jemand helfen, indem er mir in einfachen Begriffen erklärt, was die Unterschiede sind und warum ich einen über den anderen verwenden muss.

+4

Was hat die Dokumentation gesagt? Was war daran unklar? – nvoigt

Antwort

98

Diese Einstellungen können am besten anhand eines Beispiels erläutert werden. Nehmen wir an, wir möchten eine Hierarchie von Mitarbeitern in einem Unternehmen darstellen. Also machen wir eine einfache Klasse wie folgt aus:

class Employee 
{ 
    public string Name { get; set; } 
    public List<Employee> Subordinates { get; set; } 
} 

Dies ist ein kleines Unternehmen mit nur drei Mitarbeitern so weit: Angela, Bob und Charles. Angela ist der Boss, Bob und Charles sind ihre Untergebenen. Lassen Sie uns die Daten einrichten, um diese Beziehung zu beschreiben:

Employee angela = new Employee { Name = "Angela Anderson" }; 
Employee bob = new Employee { Name = "Bob Brown" }; 
Employee charles = new Employee { Name = "Charles Cooper" }; 

angela.Subordinates = new List<Employee> { bob, charles }; 

List<Employee> employees = new List<Employee> { angela, bob, charles }; 

Wenn wir die Liste der Mitarbeiter zu JSON serialisiert ...

string json = JsonConvert.SerializeObject(employees, Formatting.Indented); 
Console.WriteLine(json); 

... wir bekommen diese Ausgabe:

[ 
    { 
    "Name": "Angela Anderson", 
    "Subordinates": [ 
     { 
     "Name": "Bob Brown", 
     "Subordinates": null 
     }, 
     { 
     "Name": "Charles Cooper", 
     "Subordinates": null 
     } 
    ] 
    }, 
    { 
    "Name": "Bob Brown", 
    "Subordinates": null 
    }, 
    { 
    "Name": "Charles Cooper", 
    "Subordinates": null 
    } 
] 

So weit so gut. Sie werden jedoch feststellen, dass die Informationen für Bob und Charles im JSON wiederholt werden, da die Objekte, die sie repräsentieren, sowohl von der Hauptliste der Mitarbeiter als auch von Angelas Liste der Untergebenen referenziert werden. Vielleicht ist das für jetzt okay.

Nun nehmen wir an, wir möchten auch einen Weg haben, den Vorgesetzten jedes Mitarbeiters zusätzlich zu seinen Untergebenen zu verfolgen. Also haben wir unser Employee Modell ändern, um eine Supervisor Eigenschaft hinzufügen ...

class Employee 
{ 
    public string Name { get; set; } 
    public Employee Supervisor { get; set; } 
    public List<Employee> Subordinates { get; set; } 
} 

... und fügen Sie ein paar Zeilen mehr zu unserem Setup-Code, um anzuzeigen, dass Charles und Bob Bericht zu Angela:

Employee angela = new Employee { Name = "Angela Anderson" }; 
Employee bob = new Employee { Name = "Bob Brown" }; 
Employee charles = new Employee { Name = "Charles Cooper" }; 

angela.Subordinates = new List<Employee> { bob, charles }; 
bob.Supervisor = angela;  // added this line 
charles.Supervisor = angela; // added this line 

List<Employee> employees = new List<Employee> { angela, bob, charles }; 

Aber jetzt haben wir ein kleines Problem. Da der Objektgraph Referenzschleifen enthält (z. B. angela Referenzen bob während bob Referenzen angela), erhalten wir eine JsonSerializationException, wenn wir versuchen, die Mitarbeiterliste zu serialisieren. Ein Weg, wir, um dieses Problem zu bekommen ist durch ReferenceLoopHandling zu Ignore wie diese Einstellung:

JsonSerializerSettings settings = new JsonSerializerSettings 
{ 
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 
    Formatting = Formatting.Indented 
}; 

string json = JsonConvert.SerializeObject(employees, settings); 

Mit dieser Einstellung an Ort und Stelle, erhalten wir die folgende JSON:

[ 
    { 
    "Name": "Angela Anderson", 
    "Supervisor": null, 
    "Subordinates": [ 
     { 
     "Name": "Bob Brown", 
     "Subordinates": null 
     }, 
     { 
     "Name": "Charles Cooper", 
     "Subordinates": null 
     } 
    ] 
    }, 
    { 
    "Name": "Bob Brown", 
    "Supervisor": { 
     "Name": "Angela Anderson", 
     "Supervisor": null, 
     "Subordinates": [ 
     { 
      "Name": "Charles Cooper", 
      "Subordinates": null 
     } 
     ] 
    }, 
    "Subordinates": null 
    }, 
    { 
    "Name": "Charles Cooper", 
    "Supervisor": { 
     "Name": "Angela Anderson", 
     "Supervisor": null, 
     "Subordinates": [ 
     { 
      "Name": "Bob Brown", 
      "Subordinates": null 
     } 
     ] 
    }, 
    "Subordinates": null 
    } 
] 

Wenn Sie die JSON untersuchen, es sollte klar sein, was diese Einstellung bewirkt: Jedes Mal, wenn der Serializer eine Referenz zurück zu einem Objekt findet, das er gerade serialisiert, überspringt er einfach dieses Mitglied. (Dies verhindert, dass der Serialisierer in eine Endlosschleife gerät.) Sie können sehen, dass in Angelas Liste der Untergebenen im oberen Teil des JSON weder Bob noch Charles einen Supervisor zeigen. Im unteren Teil des JSON zeigen Bob und Charles beide Angela als ihren Vorgesetzten, aber beachten Sie, dass die Liste ihrer Untergebenen zu diesem Zeitpunkt nicht Bob und Charles einschließt.

Während es möglich ist, mit diesem JSON zu arbeiten und vielleicht sogar die ursprüngliche Objekthierarchie mit etwas Arbeit zu rekonstruieren, ist es eindeutig nicht optimal. Wir können die wiederholten Informationen in der JSON eliminieren, während immer noch die Objektreferenzen zu bewahren, indem die PreserveReferencesHandling mit Einstellung statt:

JsonSerializerSettings settings = new JsonSerializerSettings 
{ 
    PreserveReferencesHandling = PreserveReferencesHandling.Objects, 
    Formatting = Formatting.Indented 
}; 

string json = JsonConvert.SerializeObject(employees, settings); 

Jetzt bekommen wir die folgende JSON:

[ 
    { 
    "$id": "1", 
    "Name": "Angela Anderson", 
    "Supervisor": null, 
    "Subordinates": [ 
     { 
     "$id": "2", 
     "Name": "Bob Brown", 
     "Supervisor": { 
      "$ref": "1" 
     }, 
     "Subordinates": null 
     }, 
     { 
     "$id": "3", 
     "Name": "Charles Cooper", 
     "Supervisor": { 
      "$ref": "1" 
     }, 
     "Subordinates": null 
     } 
    ] 
    }, 
    { 
    "$ref": "2" 
    }, 
    { 
    "$ref": "3" 
    } 
] 

Beachten Sie, dass jetzt jedes Objekt hat hat im JSON einen sequentiellen $id Wert zugewiesen. Wenn ein Objekt das erste Mal angezeigt wird, wird es vollständig serialisiert, während nachfolgende Referenzen durch eine spezielle $ref-Eigenschaft ersetzt werden, die auf das ursprüngliche Objekt mit der entsprechenden $id verweist. Mit dieser Einstellung ist JSON viel prägnanter und kann in die ursprüngliche Objekthierarchie ohne zusätzliche Arbeit deserialisiert werden, vorausgesetzt, Sie verwenden eine Bibliothek, die die von Json.Net/Web API erzeugte $id und $ref Notation versteht.

Warum also wählen Sie die eine oder die andere Einstellung? Es hängt natürlich von Ihren Bedürfnissen ab. Wenn JSON von einem Client konsumiert wird, der das $id/$ref Format nicht versteht, und es tolerieren kann, unvollständige Daten an einigen Stellen zu haben, würden Sie ReferenceLoopHandling.Ignore verwenden. Wenn Sie nach einem kompakteren JSON suchen und Json.Net oder Web API (oder eine andere kompatible Bibliothek) zum Deserialisieren der Daten verwenden möchten, wählen Sie PreserveReferencesHandling.Objects. Wenn Ihre Daten ein gerichteter azyklischer Graph ohne doppelte Referenzen sind, brauchen Sie keine Einstellung.

+9

Ausgezeichnete Antwort. Bei der Verwendung der letzten Methode ('PreserveReferencesHandling.Objects') ist [JsonNetDecycle] (https://bitbucket.org/smithkl42/jsonnetdecycle) eine großartige Bibliothek, um die Objektreferenzen clientseitig zusammenzusetzen. – WimpyProgrammer

+0

Es ist wirklich eine gute Antwort. Ich bin erleuchtet. –

Verwandte Themen