В чем разница между PreserveReferencesHandling и ReferenceLoopHandling в Json.Net?

Я рассматриваю один образец приложения WebAPI, который имеет этот код:

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

и другой с этим кодированным:

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

Ни объясните, почему каждый выбран. Я очень новичок в WebAPI, поэтому кто-то может помочь, объяснив мне простыми словами, каковы различия и почему мне может понадобиться использовать один над другим.

Эти параметры лучше всего объяснить на примере. Предположим, что мы хотим представлять иерархию сотрудников в компании. Поэтому мы делаем простой class следующим образом:

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

Это небольшая компания с тремя сотрудниками: Анджела, Боб и Чарльз. Анджела – босс, а Боб и Чарльз – ее подчиненные. Давайте создадим данные для описания этих отношений:

 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 { bob, charles }; List employees = new List { angela, bob, charles }; 

Если мы сериализуем список сотрудников в JSON …

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

… мы получаем этот результат:

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

Все идет нормально. Однако вы заметите, что информация для Боба и Чарльза повторяется в JSON, потому что объекты, представляющие их, ссылаются как на главный список сотрудников, так и на список подчиненных Анжелы. Возможно, сейчас все в порядке.

Теперь предположим, что мы также хотели бы иметь возможность следить за каждым надзирателем Employee в дополнение к своим подчиненным. Поэтому мы меняем модель Employee чтобы добавить свойство Supervisor

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

… и добавьте еще пару строк в наш установочный код, чтобы показать, что Чарльз и Боб докладывают Анджеле:

 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 { bob, charles }; bob.Supervisor = angela; // added this line charles.Supervisor = angela; // added this line List employees = new List { angela, bob, charles }; 

Но теперь у нас есть проблема. Поскольку в графе объектов есть ссылочные петли (например, angela ссылки bob а ссылки bob angela ), мы получим JsonSerializationException когда мы попытаемся сериализовать список сотрудников. Один из способов, которым мы можем обойти эту проблему, – это установить для параметра ReferenceLoopHandling значение Ignore следующим образом:

 JsonSerializerSettings settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, Formatting = Formatting.Indented }; string json = JsonConvert.SerializeObject(employees, settings); 

С этой настройкой мы получаем следующий 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 } ] 

Если вы изучите JSON, должно быть ясно, что делает этот параметр: в любое время, когда сериализатор встречает ссылку на объект, который уже находится в процессе сериализации, он просто пропускает этот элемент. (Это не позволяет сериализатору попасть в бесконечный цикл.) Вы можете видеть, что в списке подчиненных Анжелы в верхней части JSON ни Боб, ни Чарльз не показывают супервизора. В нижней части JSON Боб и Чарльз показывают Анджелу в качестве своего руководителя, но заметят, что ее список подчиненных в этот момент не включает в себя как Боба, так и Чарльза.

Хотя можно работать с этим JSON и, возможно, даже восстановить исходную иерархию объектов от него с некоторой работой, это явно не оптимально. Мы можем устранить повторяющуюся информацию в JSON, сохраняя при этом ссылки на объекты, используя вместо этого параметр PreserveReferencesHandling :

 JsonSerializerSettings settings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects, Formatting = Formatting.Indented }; string json = JsonConvert.SerializeObject(employees, settings); 

Теперь мы получаем следующий 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" } ] 

Обратите внимание, что теперь каждому объекту присваивается последовательное значение $id в JSON. В первый раз, когда появляется объект, он сериализуется полностью, а последующие ссылки заменяются специальным значением $ref которое ссылается на исходный объект с соответствующим $id . Благодаря этой настройке JSON гораздо более кратким и может быть десериализован обратно в исходную иерархию объектов без дополнительной работы, если вы используете библиотеку, которая понимает нотацию $id и $ref выпущенную Json.Net / Web API.

Так почему бы вам выбрать одну настройку или другую? Конечно, это зависит от ваших потребностей. Если JSON будет потребляться клиентом, который не понимает формат $id / $ref , и он может терпеть наличие неполных данных в местах, вы бы решили использовать ReferenceLoopHandling.Ignore . Если вы ищете более компактный JSON, и вы будете использовать Json.Net или Web API (или другую совместимую библиотеку) для десериализации данных, тогда вы захотите использовать PreserveReferencesHandling.Objects . Если ваши данные представляют собой ориентированный ациклический граф без дубликатов ссылок, вам не нужно ни устанавливать.

  • Json.NET Отключить десериализацию в DateTime
  • Как я могу разобрать строку JSON, которая приведет к незаконным идентификаторам C #?
  • Разбор массива JSON с использованием Json.Net
  • Как программно выбирать конструктор во время десериализации?
  • JsonValueProviderFactory выбрасывает «запрос слишком большой»
  • Как использовать JSON.NET для десериализации в вложенный / рекурсивный словарь и список?
  • Уничтожить stream массива json по одному элементу за раз
  • Как я могу десериализовать JSON для простого словаря в ASP.NET?
  • Что такое эквивалент JSON.NET XPath, SelectNodes XML, SelectSingleNode?
  • JToken: получить исходное / оригинальное значение JSON
  • Использование преобразователей Json.NET для десериализации свойств
  • Давайте будем гением компьютера.