When trying to deserialize some Json using Newtonsoft\'s Json.NET nuget library my collection-properties are null when I do NOT provide a JsonSerializ
Your problem is that the property you are trying to deserialize gets and sets a proxy collection rather than a "real" collection in your object graph. Doing so falls afoul of an implementation decision about the algorithm Json.NET uses to construct and populate properties returning reference type objects, collections and dictionaries. That algorithm is:
It calls the getter in the parent class to get the current value of the property being deserialized.
If null, and unless a custom constructor is being used, it allocates an instance of the property's returned type (using the JsonContract.DefaultCreator method for the type).
It calls the setter in the parent to set the allocated instance back into the parent.
It proceeds to populate the instance of the type.
It does not set the instance back a second time, after it has been populated.
Calling the getter at the beginning makes it possible to populate an instance of a type (e.g. with JsonConvert.PopulateObject()), recursively populating any pre-allocated instances of other types to which the type refers. (That's the purpose of the existingValue
parameter in JsonConverter.ReadJson().)
However, the ability to populate a pre-allocated object graph in this manner comes with a cost: each object encountered in the graph must be the real object and not some proxy object created for serialization purposes. If the object returned by the getter is just some proxy, the proxy will get populated, and not the "real" object - unless the proxy has some sort of mechanism to pass changes to its data back to its originator.
(While this decision may seem undesirable, it's not unusual; XmlSerializer
works the same way. For a list of serializers that do this, see XML Deserialization of collection property with code defaults.)
ObjectCreationHandling = ObjectCreationHandling.Replace, as you have observed, changes this algorithm, so that collections are allocated, populated, and set back. That is one way to enable deserialization of proxy collections.
As another workaround, you could choose instead to serialize and deserialize a proxy array:
[JsonIgnore]
public List<Media> Images
{
get { return new List<Media> { new Media{..}, new Media{..} }; }
set { AddImages(value); }
}
[JsonProperty("Images")] // Could be private
Media [] ImagesArray
{
get { return Images.ToArray(); }
set { AddImages(value); }
}
For arrays, Json.NET (and XmlSerializer
) must call the setter after the array is fully read, since the size cannot be known until fully read, and so the array cannot be allocated and set back until fully read.
(You could also do tricks with a proxy ObservableCollection
, but I wouldn't recommend it.)