wake-up-neo.com

Was ist der Unterschied zwischen PreserveReferencesHandling und ReferenceLoopHandling in Json.Net?

Ich sehe mir ein WebAPI-Anwendungsbeispiel mit folgendem Code an:

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

und ein anderer mit diesem Code:

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

Weder erklären, warum jeder gewählt wird. Ich bin sehr neu in WebAPI, daher kann mir jemand helfen, indem er mir in einfachen Worten erklärt, worin die Unterschiede bestehen und warum ich sie möglicherweise übereinander verwenden muss.

57
user3568783

Diese Einstellungen lassen sich am besten anhand eines Beispiels erklären. Angenommen, wir möchten eine Hierarchie von Mitarbeitern in einem Unternehmen darstellen. Also machen wir eine einfache Klasse wie diese:

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

Dies ist ein kleines Unternehmen mit nur drei Mitarbeitern: Angela, Bob und Charles. Angela ist die Chefin, während Bob und Charles ihre Untergebenen sind. 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 serialisieren ...

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, ist es gut. Sie werden jedoch feststellen, dass die Informationen für Bob und Charles im JSON wiederholt werden, da die Objekte, die sie darstellen, sowohl von der Hauptliste der Mitarbeiter als auch von Angelas Liste der Untergebenen referenziert werden. Vielleicht ist das vorerst in Ordnung.

Nehmen wir nun an, wir möchten auch die Möglichkeit haben, den Vorgesetzten jedes Mitarbeiters zusätzlich zu seinen Untergebenen im Auge zu behalten. Also ändern wir unser Employee-Modell, um eine Supervisor -Eigenschaft hinzuzufügen ...

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

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

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 einen JsonSerializationException wenn wir versuchen, die Mitarbeiterliste zu serialisieren. Eine Möglichkeit, dieses Problem zu umgehen, besteht darin, ReferenceLoopHandling auf Ignore zu setzen:

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

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

Mit dieser Einstellung erhalten wir den folgenden 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 den JSON untersuchen, sollte klar sein, was diese Einstellung bewirkt: Jedes Mal, wenn der Serializer auf einen Verweis auf ein Objekt stößt, das bereits serialisiert wird, überspringt er einfach diesen Member. (Dies verhindert, dass der Serializer 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 anzeigen. Im unteren Teil des JSON zeigen Bob und Charles Angela als ihren Vorgesetzten, aber beachten Sie, dass ihre Untergebenenliste zu diesem Zeitpunkt nicht sowohl Bob als auch Charles enthält.

Es ist zwar möglich, mit diesem JSON zu arbeiten und möglicherweise sogar die ursprüngliche Objekthierarchie daraus mit einigem Aufwand zu rekonstruieren, dies ist jedoch eindeutig nicht optimal. Wir können die wiederholten Informationen in JSON eliminieren, während die Objektreferenzen beibehalten werden, indem wir stattdessen die Einstellung PreserveReferencesHandling verwenden:

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

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

Jetzt bekommen wir folgenden 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 jedem Objekt ein sequentieller $id - Wert in der JSON zugewiesen wurde. Wenn ein Objekt zum ersten Mal angezeigt wird, wird es vollständig serialisiert, während nachfolgende Verweise durch eine spezielle $ref - Eigenschaft ersetzt werden, die auf das ursprüngliche Objekt mit dem entsprechenden $id Verweist. Mit dieser Einstellung ist JSON wesentlich übersichtlicher und kann ohne zusätzlichen Arbeitsaufwand wieder in die ursprüngliche Objekthierarchie deserialisiert werden, vorausgesetzt, Sie verwenden eine Bibliothek, die $id Und $ref Versteht. Notation erstellt von Json.Net/Web API.

Warum sollten Sie die eine oder andere Einstellung wählen? Das hängt natürlich von Ihren Bedürfnissen ab. Wenn der JSON von einem Client verwendet wird, der das Format $id/$ref Nicht versteht und unvollständige Daten an bestimmten Stellen tolerieren kann, sollten Sie ReferenceLoopHandling.Ignore Verwenden. . Wenn Sie nach kompakterem JSON suchen und Json.Net oder Web API (oder eine andere kompatible Bibliothek) zum Deserialisieren der Daten verwenden, wählen Sie PreserveReferencesHandling.Objects. Wenn es sich bei Ihren Daten um ein gerichtetes azyklisches Diagramm ohne doppelte Verweise handelt, müssen Sie keine der beiden Einstellungen vornehmen.

133
Brian Rogers