DataContractSerializer doesn't call my constructor?

C#.NetConstructorSerializationDatacontractserializer

C# Problem Overview


I just realized something crazy, which I assumed to be completely impossible : when deserializing an object, the DataContractSerializer doesn't call the constructor !

Take this class, for instance :

[DataContract]
public class Book
{
    public Book()
    { // breakpoint here
    }

    [DataMember(Order = 0)]
    public string Title { get; set; }
    [DataMember(Order = 1)]
    public string Author { get; set; }
    [DataMember(Order = 2)]
    public string Summary { get; set; }
}

When I deserialize an object of that class, the breakpoint is not hit. I have absolutely no idea how it is possible, since it is the only constructor for this object !

I assumed that perhaps an additional constructor was generated by the compiler because of the DataContract attribute, but I couldn't find it through reflection...

So, what I'd like to know is this : how could an instance of my class be created without the constructor being called ??

NOTE: I know that I can use the OnDeserializing attribute to initialize my object when deserialization begins, this is not the subject of my question.

C# Solutions


Solution 1 - C#

DataContractSerializer (like BinaryFormatter) doesn't use any constructor. It creates the object as empty memory.

For example:

    Type type = typeof(Customer);
    object obj = System.Runtime.Serialization.
        FormatterServices.GetUninitializedObject(type);

The assumption is that the deserialization process (or callbacks if necessary) will fully initialize it.

Solution 2 - C#

There are some scenario's that wouldn’t be possible without this behavior. Think of the following:

  1. You have an object that has one constructor that sets the new instance to an "initialized" state. Then some methods are called on that instance, that bring it in a "processed" state. You don’t want to create new objects having the "processed" state, but you still want de serialize / deserialize the instance.

  2. You created a class with a private constructor and some static properties to control a small set of allowed constructor parameters. Now you can still serialize / deserialize them.

XmlSerializer has the behavior you expected. I have had a some problems with the XmlSerializer because it DOES need a default constructor. Related to that, sometimes it makes sense to have private property setters. But the XmlSerializer also needs public getter and setter on properties in order to serialize / deserialize.

I think of the DataContractSerializer / BinaryFormatter behavior like suspending the state of an instance during serialization and resuming during deserialization. In other words, the instances are not “constructed” but “restored” to an earlier state.

As you already mentioned, the [OnDeserializing] attribute makes it possible to keep non serialized data in sync.

Solution 3 - C#

FWIW, you can call the constructor explicitly from a [OnDeserializing] method:

[OnDeserializing]
public void OnDeserializing(StreamingContext context)
{
    this.GetType().GetConstructor(System.Array.Empty<Type>()).Invoke(this, null);
}

Solution 4 - C#

Use [OnDeserialized] attribute to initialise your properties.

// This method is called after the object
// is completely deserialized. Use it instead of the
// constructror.
[OnDeserialized]
void OnDeserialized(StreamingContext context)
{
    fullName = firstName + " " + lastName;
}

Please refer to microsoft guid-lines: https://docs.microsoft.com/en-us/dotnet/standard/serialization/serialization-guidelines

Solution 5 - C#

In my case, i wanted to create an object to use in a lock-clause. I tried implementing IDeserializationCallback (didn't work because callback only runs after properties have been assigned), [OnDeserialized] (didn't work, same previous reason), and ISerializable (didn't work because the class is decorated with the [DataContractAttribute]).

My workaround was to initialize the field before it's used using Interlocked.CompareExchange. A bit of unnecessary work gets done, but at least now my field gets initialized when a DataContractSerializer creates it.

Interlocked.CompareExchange(ref _sync, new object(), null);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionThomas LevesqueView Question on Stackoverflow
Solution 1 - C#Marc GravellView Answer on Stackoverflow
Solution 2 - C#BasvdLView Answer on Stackoverflow
Solution 3 - C#Bruno MartinezView Answer on Stackoverflow
Solution 4 - C#Temala RidhaView Answer on Stackoverflow
Solution 5 - C#hookah kidView Answer on Stackoverflow