Search code examples
c#json.net-coreserializationsystem.text.json

.NET 8 serialization/deserialization of IEnumerable interface


Having following classes:

public class Person
{
    public int Age { get; set; }
}
using System.Collections;
using System.Text.Json.Serialization;

namespace SerializeCustomEnumerable;

public class Statistics<T> : IEnumerable<(int Year, T Value)> where T : new()
{
        // Underlying structure where data are stored 
        protected Dictionary<int, T> Years { get; set; } = new Dictionary<int, T>();
        
        public Statistics() { }

        public Statistics(Dictionary<int, T> years)
        {
            Years = years ?? throw new ArgumentNullException(nameof(years));
        }

        public T this[int year]
        {
            get
            {
                return Years.TryGetValue(year, out var value) ? value : new T();
            }
            set
            {
                Years[year] = value;
            }
        }

        public IEnumerator<(int Year, T Value)> GetEnumerator()
        {
            foreach (var element in Years)
            {
                yield return (element.Key, element.Value);
            }
    
        }

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

I need somehow to be able to serialize/deserialize Statistics<T> class using System.Text.Json. This serialized object should look like serialized Dictionary in json.

Here is a sample code with comments of what I need

using System.Text.Json;
using SerializeCustomEnumerable;

JsonSerializerOptions jSerOptions = new JsonSerializerOptions()
{
    PropertyNameCaseInsensitive = true,
    IncludeFields = true
                
};

//populate our statistics object
var firstPerson = new Person() { Age = 5 };
var secondPerson = new Person() { Age = 10 };
var dict = new Dictionary<int, Person>()
{
    [0] = firstPerson,
    [1] = secondPerson
};
var simplestat = new Statistics<Person>(dict);
  


//this is what the json should look like
string expectedJson = JsonSerializer.Serialize(dict, jSerOptions);
//this is what I get
string serialized = JsonSerializer.Serialize(simplestat, jSerOptions);

//and also here deserialization fails... :(
var result = JsonSerializer.Deserialize<Statistics<Person>>(serialized, jSerOptions);

But there is a catch to it. I can't use Converters in JsonSerializerOptions. (The reason is that this serialization is being done in different code base which is not managed by me. I know only that they call JsonSerializer.Serialize(dict, jSerOptions); )


Solution

  • Probably the easiest option would be to just implement IDictionary<TKey, TValue>:

    public class Statistics<T> : IEnumerable<(int Year, T Value)>, IDictionary<int, T> where T : new()
    {
       // ...
    }
    

    And direct all the interface members calls to Years. If you want you can make IDictionary interface implementation to be an explicit one.

    The second option would be to implement a custom converter.

    Also maybe useful - Deserialize a class that implements IEnumerable (though a bit different case)