Recently I've come across the need to be able to serialize and deserialize objects with non-default constructors using JSON.NET. One way of doing this is to add JsonConstructor attribute to one (1) constructor and make sure that all parameter names does match the public properties names. But the problem arise when you don’t have control over the classes that should be serialized/deserialize, or just don't want to add JSON specific constructors mudding your objects.
So the other solution would be to have a fallback method whenever JSON.NET cannot find an appropriate constructor, giving an opportunity to analyze the class and call the appropriate constructor returning an instance of the newly created object.
Here follows example code of how it works,
public static T ReadJsonFile(string fileName) {
if( File.Exists(fileName) ) {
var s = new JsonSerializerSettings {
ConstructorHandlingFallback = CtorFallback
};
return (T)JsonConvert.DeserializeObject(File.ReadAllText(fileName), typeof(T), s);
}
return default(T);
}
// Create new object instance using default values for constructor parameters
private static void CtorFallback(object sender, ConstructorHandlingFallbackEventArgs e) {
List<object> args = new List<object>();
foreach( var p in e.ObjectContract.Properties ) {
args.Add(p.PropertyType.IsValueType ? Activator.CreateInstance(p.PropertyType) : null);
}
e.Object = Activator.CreateInstance(e.ObjectContract.UnderlyingType, args.ToArray());
e.Handled = true;
}
The modified JSON.NET library is a part of the Service Bus MQ Manager project at GitHub, the compiled version can be downloaded below, until this gets implemented in future versions of JSON.NET.
UPDATE 2013-10-06:
The project is now available as a Fork at GitHub updated to v5.0 R6
Newtonsoft.Json.zip v4.5 r11 (479.28 kb)
This also solves the exception "Unable to find a constructor to use for type 'xxx'. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute" that JSON.NET throws where there are more then one parameterized public constructors and none public default constructor.