Pierluc SS Pierluc SS - 11 months ago 81
C# Question

How to serialize/deserialize a custom collection with additional properties using Json.Net

I have a custom collection (implements IList) which has some custom properties as shown below:

class FooCollection : IList<Foo> {

private List<Foo> _foos = new List<Foo>();
public string Bar { get; set; }

//Implement IList, ICollection and IEnumerable members...


When I serialize, I use the following code:

JsonSerializerSettings jss = new JsonSerializerSettings() {
TypeNameHandling = TypeNameHandling.Auto
string serializedCollection = JsonConvert.SerializeObject( value , jss );

It serializes and deserializes all the collection items properly; however, any extra properties in the
class are not taken into account.

Is there anyway to include them in the serialization?

Answer Source

The problem is the following: when an object implements IEnumerable, JSON.net identifies it as an array of values and serializes it following the array Json syntax (that does not include properties), e.g. :

 [ {"FooProperty" : 123}, {"FooProperty" : 456}, {"FooProperty" : 789}]

If you want to serialize it keeping the properties, you need to handle the serialization of that object by hand by defining a custom JsonConverter :

// intermediate class that can be serialized by JSON.net
// and contains the same data as FooCollection
class FooCollectionSurrogate
    // the collection of foo elements
    public List<Foo> Collection { get; set; }
    // the properties of FooCollection to serialize
    public string Bar { get; set; }

public class FooCollectionConverter : JsonConverter
    public override bool CanConvert(Type objectType)
        return objectType == typeof(FooCollection);

    public override object ReadJson(
        JsonReader reader, Type objectType, 
        object existingValue, JsonSerializer serializer)
        // N.B. null handling is missing
        var surrogate = serializer.Deserialize<FooCollectionSurrogate>(reader);
        var fooElements = surrogate.Collection;
        var fooColl = new FooCollection { Bar = surrogate.Bar };
        foreach (var el in fooElements)
        return fooColl;

    public override void WriteJson(JsonWriter writer, object value, 
                                   JsonSerializer serializer)
        // N.B. null handling is missing
        var fooColl = (FooCollection)value;
        // create the surrogate and serialize it instead 
        // of the collection itself
        var surrogate = new FooCollectionSurrogate() 
            Collection = fooColl.ToList(), 
            Bar = fooColl.Bar 
        serializer.Serialize(writer, surrogate);

Then use it as follows:

var ss = JsonConvert.SerializeObject(collection, new FooCollectionConverter());

var obj = JsonConvert.DeserializeObject<FooCollection>(ss, new FooCollectionConverter());