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 FooCollection
class are not taken into account.
Is there anyway to include them in the serialization?
In Deserialization, it does the opposite of Serialization which means it converts JSON string to custom . Net object. In the following code, it calls the static method DeserializeObject() of the JsonConvert class by passing JSON data. It returns a custom object (BlogSites) from JSON data.
JSON is a format that encodes objects in a string. Serialization means to convert an object into that string, and deserialization is its inverse operation (convert string -> object).
Json.NET has excellent support for serializing and deserializing collections of objects. To serialize a collection - a generic list, array, dictionary, or your own custom collection - simply call the serializer with the object you want to get JSON for.
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)
fooColl.Add(el);
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());
Personally I like to avoid writing custom JsonConverter
s where possible, and instead make use of the various JSON attributes which were designed for this purpose. You can simply decorate FooCollection
with JsonObjectAttribute
, which forces serialization as a JSON object rather than an array. You'd have to decorate the Count
and IsReadOnly
properties with JsonIgnore
to prevent them from showing up in the output. If you want to keep _foos
a private field, you would also have to decorate it with JsonProperty
.
[JsonObject]
class FooCollection : IList<Foo> {
[JsonProperty]
private List<Foo> _foos = new List<Foo>();
public string Bar { get; set; }
// IList implementation
[JsonIgnore]
public int Count { ... }
[JsonIgnore]
public bool IsReadOnly { ... }
}
Serializing yields something like the following:
{
"_foos": [
"foo1",
"foo2"
],
"Bar": "bar"
}
Obviously this only works if you are able to change the definition of FooCollection
in order to add those attributes, otherwise you have to go the way of custom converters.
If you don't want to write custom JsonConverter
or use JSON attributes (JsonObjectAttribute), you could use following extension method:
public static string ToFooJson<T>(this FooCollection fooCollection)
{
return JsonConvert.SerializeObject(new
{
Bar = fooCollection.Bar,
Collection = fooCollection
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With