I\'m using Json.NET in my WCF data service.
Here\'s my class (simplified):
[DataContract]
public class Component
{
public Component()
{
Others already mentioned the second constructor, but using 2 attributes: [JsonConstructor] and [Obsolete] you can do much better than leaving it up to humans to remember which one to call.
public ChatMessage()
{
MessageID = ApplicationState.GetNextChatMessageID(); // An expensive call that uses up an otherwise free ID from a limited set and does disk access in the process.
}
[JsonConstructor] // This forces JsonSerializer to call it instead of the default.
[Obsolete("Call the default constructor. This is only for JSONserializer", true)] // To make sure that calling this from your code directly will generate a compiler error. JSONserializer can still call it because it does it via reflection.
public ChatMessage(bool DO_NOT_CALL_THIS)
{
}
[JsonConstructor] forces JsonSerializer to call it instead of the default.
[Obsolete("...", true)] Makes sure that calling this from your code directly will generate a compiler error. JSONserializer can still call it because it does it via reflection.
You could create a class that inherits from CustomCreationConverter
and use FormatterServices.GetSafeUninitializedObject
to create your
object. It skips calling the constructor.
More about CustomCreationConverter here.
Placing
[JsonObject(MemberSerialization.Fields)]
on a class will make Json.NET use
FormatterServices.GetSafeUninitializedObject
by default (although
Fields mode will also serialize public/private fields rather than
public properties which you may not want).
Move the logic you don't want run outside of the default constructor.
A constructor is always invoked. I usually have two constructors. One for serialization (the default constructor) and one for all "regular" code:
[DataContract]
public class Component
{
// for JSON.NET
protected Component()
{
}
public Component(allMandatoryFieldsHere)
{
// I'm doing some magic here.
}
}
In that way I can also make sure that the dev specify all information which are required.
However, I do not really recommend that you use anything but DTO's when transfering information since it's otherwise possible to circumvent the encapsulation of your objects (anyone could initialize any field with any value). Well. If you use anything but anemic models.
Using FormatterServices.GetSafeUninitializedObject
is imho therefore an ugly workaround, since no one can tell that you create all objects in an unintialized way. Constructor initialization is there for a reason. It's better that the classes can tell that it's OK to not call the real constructor by providing a "serialization" constructor as I suggested.
The best option to avoid constructor calls on deserialization is to create special contract resolver that overrides creator function for all classes without constructor marked with JsonConstructor attribute. This way you still can force JSON.NET to call constructor if you really need it, but all other classes will be created much like in standard DataContract serializers in .NET. Here is the code:
/// <summary>
/// Special contract resolver to create objects bypassing constructor call.
/// </summary>
public class NoConstructorCreationContractResolver : DefaultContractResolver
{
/// <summary>
/// Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> for the given type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// A <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> for the given type.
/// </returns>
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
// prepare contract using default resolver
var objectContract = base.CreateObjectContract(objectType);
// if type has constructor marked with JsonConstructor attribute or can't be instantiated, return default contract
if (objectContract.OverrideConstructor != null || objectContract.CreatedType.IsInterface || objectContract.CreatedType.IsAbstract)
return objectContract;
// prepare function to check that specified constructor parameter corresponds to non writable property on a type
Func<JsonProperty, bool> isParameterForNonWritableProperty =
parameter =>
{
var propertyForParameter = objectContract.Properties.FirstOrDefault(property => property.PropertyName == parameter.PropertyName);
if (propertyForParameter == null)
return false;
return !propertyForParameter.Writable;
};
// if type has parameterized constructor and any of constructor parameters corresponds to non writable property, return default contract
// this is needed to handle special cases for types that can be initialized only via constructor, i.e. Tuple<>
if (objectContract.ParametrizedConstructor != null
&& objectContract.ConstructorParameters.Any(parameter => isParameterForNonWritableProperty(parameter)))
return objectContract;
// override default creation method to create object without constructor call
objectContract.DefaultCreatorNonPublic = false;
objectContract.DefaultCreator = () => FormatterServices.GetSafeUninitializedObject(objectContract.CreatedType);
return objectContract;
}
}
All you need is simply set this contract resolver in serializer settings before deserialization.