Список десериализации Json.net дает повторяющиеся элементы
Я только что начал использовать Newtonsoft.Json(Json.net). В моем первом простом испытании я столкнулся с проблемой при десериализации общих списков. В моем примере кода ниже я сериализую объект, содержащий три типа простых целых списков (свойство, член var и массив).
Получившийся json выглядит отлично (списки преобразуются в json-массивы). Однако, когда я десериализую json обратно на новый объект того же типа, все элементы списка дублируются, ожидайте для массива. Я проиллюстрировал это, сериализуя его во второй раз.
От поиска, я прочитал, что может быть поле поддержки "private" для списков, которые также заполняет десериализатор.
Итак, мой вопрос: есть ли (желательно простой) способ избежать дублирования элементов в следующем случае?
Код
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace JsonSerializeExample
{
public class Program
{
static void Main()
{
var data = new SomeData();
var json = JsonConvert.SerializeObject(data);
Console.WriteLine("First : {0}", json);
var data2 = JsonConvert.DeserializeObject<SomeData>(json);
var json2 = JsonConvert.SerializeObject(data2);
Console.WriteLine("Second: {0}", json2);
}
}
public class SomeData
{
public string SimpleField;
public int[] IntArray;
public IList<int> IntListProperty { get; set; }
public IList<int> IntListMember;
public SomeData()
{
SimpleField = "Some data";
IntArray = new[] { 7, 8, 9 };
IntListProperty = new List<int> { 1, 2, 3 };
IntListMember = new List<int> { 4, 5, 6 };
}
}
}
Результирующий выход
First : {"SimpleField":"Some data","IntArray":[7,8,9],"IntListMember":[4,5,6],"IntListProperty":[1,2,3]}
Second: {"SimpleField":"Some data","IntArray":[7,8,9],"IntListMember":[4,5,6,4,5,6],"IntListProperty":[1,2,3,1,2,3]}
Здесь может быть несколько совпадений с Json.Net дублирует личный список элементов. Тем не менее, я думаю, что моя проблема еще проще, и я до сих пор не понял этого.
Ответы
Ответ 1
Это потому, что вы добавляете элементы в конструктор. Обычный подход в десериализаторах при обработке списка в основном:
- прочитать список через getter
- если список равен null: создайте новый список и назначьте его с помощью средства настройки свойств, если один
- десериализуйте каждый элемент по очереди и добавьте (
Add
) в список
это происходит потому, что большинство членов списка не имеют сеттеров, т.е.
public List<Foo> Items {get {...}} // <=== no set
Контрастность к массивам, которая должна иметь полезность сеттера; поэтому подход обычно:
- десериализуйте каждый элемент по очереди и добавьте (
Add
) во временный список
- преобразовать список в массив (
ToArray
) и назначить через setter
Некоторые сериализаторы дают вам возможность контролировать это поведение (другие нет); и некоторые сериализаторы дают вам возможность полностью обойти конструктор (другие - нет).
Ответ 2
Я столкнулся с аналогичной проблемой с другой основной причиной. Я сериализовал и десериализовал класс, который выглядел следующим образом:
public class Appointment
{
public List<AppointmentRevision> Revisions { get; set; }
public AppointmentRevision CurrentRevision
{
get { return Revision.LastOrDefault(); }
}
public Appointment()
{
Revisions = new List<AppointmentRevision>();
}
}
public class AppointmentRevision
{
public List<Attendee> Attendees { get; set; }
}
Когда я сериализовал это, CurrentRevision также сериализовался. Я не знаю, как это сделать, но когда он десериализован, он правильно сохранял один экземпляр AppointmentRevision, но создавал дубликаты в списке участников. Решение заключалось в использовании атрибута JsonIgnore в свойстве CurrentRevision.
public class Appointment
{
public List<AppointmentRevision> Revisions { get; set; }
[JsonIgnore]
public AppointmentRevision CurrentRevision
{
get { return Revision.LastOrDefault(); }
}
public Appointment()
{
Revisions = new List<AppointmentRevision>();
}
}
Ответ 3
Как применить ObjectCreationHandling.Replace к выбранным свойствам при десериализации JSON?
Оказывается (я в 2019 году), вы можете установить элементы списка в своем конструкторе, как вы делали в своем вопросе. Я добавил атрибут ObjectCreationHandling.Replace над объявлением списка, после чего сериализация должна заменить все, что хранится в списке, на JSON.