Json.net serialization of custom collection implementing IEnumerable<T>

I have a collection class that implements IEnumerable and I am having trouble deserializing a serialized version of the same. I am using Json.net v 4.0.2.13623

Here is a simplier version of my collection class that illustrates my issue

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
  }
}

When I serialize it i get the following json

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

But then when i try to deserialize i get the following exception System.Exception: Cannot create and populate list type MyTypes

Have also tried using serialization hints but have had no success

These are the test function i have used

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);
}

Has anybody managed to serialize their own collection objects ?

Thanks in advance

Pat


Solution 1:

As of Json.NET 6.0 Release 3 and later (I tested on 8.0.3), this works automatically as long as the custom class implementing IEnumerable<MyType> has a constructor that takes an IEnumerable<MyType> input argument. From the release notes:

To all future creators of immutable .NET collections: If your collection of T has a constructor that takes IEnumerable then Json.NET will automatically work when deserializing to your collection, otherwise you're all out of luck.

Since the MyTypes class in the question has just such a constructor, this now just works. Demonstration fiddle: https://dotnetfiddle.net/LRJHbd.

Absent such a constructor, one would need to add a parameterless constructor and implement ICollection<MyType> (instead of just IEnumerable<MyType>) with a working Add(MyType item) method. Json.NET can then construct and populate the collection. Or deserialize to an intermediate collection as in the original answer.

Solution 2:

I believe JSON .NET depends on having a parameterless constructor for the types it deserializes to. From a deserialization point of view it has no idea what to provide to the constrcutor.

As for your collection, again how is it supposed to know how to populate an implementation of IEnumerable<T> which only defines how to enumerate the collection not how to populate it. You should instead deserialize directly to some standard IEnumerable<MyType> (or directly to IEnumerable<MyType> which I believe JSON.NET supports) and then pass it to your constructor.

I believe all of the following should work in this case:

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));

Alternatively, though more work, if you need to deserialized directly you can implement something like IList on your collection which should allow JSON.NET to use the IList methods to populate your collection.