skip to Main Content

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

2

Answers


  1. 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.

    Login or Signup to reply.
  2. It does seem like system.text.json will not serialize value tuples by default.

    So you could try specify the serialization options to include fields:

    var options = new JsonSerializerOptions
    {
        IncludeFields = true,
    };
    

    Or you could use Tuple<int , T > instead of (int Year, T Value). Do note that the names in value tuples are compile time only. So in the json either option will result in "Item1": 1, "Item2": { "Age": 42 }

    The second problem is that the library does not know how to create or initialize your custom Statistics<T>-class. So you probably need to deserialize to a collection of a known type: JsonSerializer.Deserialize<IEnumerable<(int Year, Person Value)>>(serialized, jSerOptions);. Unless you write your own converter or find some other way to make the library aware of how to handle your custom collection.

    Note that it is generally a good idea to keep objects intended for serialization as simple as possible. It may also be a good idea to keep a separate set of types specifically for serialization, i.e. (DTOs). That way you have more flexibility to handle difficult to serialize data, or changes to the data structure. I would be somewhat concerned that your Statistics<T> does a bit much to be suitable for serialization.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search