Сериализация Json.net пользовательской коллекции, реализующая IEnumerable <T>

У меня есть класс коллекции, который реализует IEnumerable, и у меня возникает проблема десериализации сериализованной версии. Я использую Json.net v 4.0.2.13623

Вот более простая версия моего класса коллекции, которая иллюстрирует мою проблему

public class MyType
{
  public int Number { get; private set; }
  public MyType(int number)
  {
    this.Number = number;
  }
}

public class MyTypes : IEnumerable<MyType>
{
  private readonly Dictionary<int, MyType> c_index;
  public MyTypes(IEnumerable<MyType> seed)
  {
    this.c_index = seed.ToDictionary(item => item.Number);
  }
  public IEnumerator<MyType> GetEnumerator()
  {
    return this.c_index.Values.GetEnumerator();
  }
  IEnumerator IEnumerable.GetEnumerator()
  {
    return this.GetEnumerator();
  }
  public int CustomMethod()
  {
    return 1;  // Just for illustration
  }
}

Когда я сериализую его, я получаю следующий json

[
  {
    "Number": 1
  },
  {
    "Number": 2
  }
]

Но тогда, когда я пытаюсь десериализовать, я получаю следующее исключение System.Exception: невозможно создать и заполнить тип списка MyTypes

Также попробовали использовать подсказки сериализации, но не имели успеха

Это тестовая функция, которую я использовал

public void RoundTrip()
{
  var _myTypes1 = new MyTypes(new[] { new MyType(1), new MyType(2) });
  var _jsonContent = JsonConvert.SerializeObject(_myTypes1, Formatting.Indented);
  var _myTypes2 = JsonConvert.DeserializeObject<MyTypes>(_jsonContent);
}
public void RoundTripWithSettings()
{
  var _myTypes1 = new MyTypes(new[] { new MyType(1), new MyType(2) });

  var _serializationSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Arrays };
  var _jsonContent = JsonConvert.SerializeObject(_myTypes1, Formatting.Indented, _serializationSettings);
  var _myTypes2 = JsonConvert.DeserializeObject<MyTypes>(_jsonContent, _serializationSettings);
}

Кто-нибудь смог сериализовать свои собственные объекты коллекции?

Заранее спасибо

Пат

Ответ 1

Я считаю, что JSON.NET зависит от наличия безпараметрического конструктора для типов, которые он десериализует. С точки зрения десериализации она не имеет понятия, что предоставить конструктору.

Что касается вашей коллекции, снова как она должна знать, как заполнить реализацию IEnumerable<T>, которая определяет только, как перечислять коллекцию, а не как ее заполнять. Вы должны вместо этого десериализоваться непосредственно на некоторый стандартный IEnumerable<MyType> (или непосредственно на IEnumerable<MyType>, который, как я полагаю, поддерживает JSON.NET), а затем передать его вашему конструктору.

Я считаю, что в этом случае должно работать следующее:

var _myTypes2 = new MyTypes(JsonConvert.DeserializeObject<MyTypes[]>(_jsonContent));
var _myTypes2 = new MyTypes(JsonConvert.DeserializeObject<List<MyTypes>>(_jsonContent));
var _myTypes2 = new MyTypes(JsonConvert.DeserializeObject<IEnumerable<MyTypes>>(_jsonContent));

В качестве альтернативы, хотя больше работы, если вам нужно десериализовать напрямую, вы можете реализовать что-то вроде IList в своей коллекции, что должно позволить JSON.NET использовать методы IList для заполнения вашей коллекции.

Ответ 2

Как и в Json.NET 6.0 Release 3 и более поздних версиях (я тестировал 8.0.3), это работает автоматически, пока пользовательский класс, реализующий IEnumerable<MyType>, имеет конструктор, который принимает входной аргумент IEnumerable<MyType>. Из примечания к выпуску:

Для всех будущих создателей неизменяемых коллекций .NET: если ваша коллекция T имеет конструктор, который принимает IEnumerable, то Json.NET будет автоматически работать при десериализации в вашу коллекцию, иначе вам все не повезло.

Поскольку класс MyTypes в вопросе имеет только такой конструктор, теперь это работает. Демонстрационная скрипка: https://dotnetfiddle.net/LRJHbd.

При отсутствии такого конструктора нужно добавить конструктор без параметров и реализовать ICollection<MyType> (вместо простого IEnumerable<MyType>) с помощью рабочего метода Add(MyType item). Затем Json.NET может создавать и заполнять коллекцию. Или десериализовать промежуточную коллекцию, как в исходном ответе .