Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialization constructor not called

I am attempting to serialize/deserialize an object that contains a Dictionary<Tuid,Section>. These are both custom types.

In my code I have a type of Template which contains the Dictionary<Tuid,Section>. It is the Template class that I am attempting to serialize/deserialze.

To resolve the problem that this collection is a Dictionary I have implemented the ISerializable interface on my Template class....

[Serializable]
public class Template : ISerializable
{
    protected Template(SerializationInfo info, StreamingContext context)
    {
        // Deserialize the sections
        List<Tuid> tuids = (List<Tuid>)info.GetValue("Sections_Keys", typeof(List<Tuid>));
        List<Section> sections = (List<Section>)info.GetValue("Sections_Values", typeof(List<Section>));
        this._sections = new Dictionary<Tuid, Section>();

        for (int i = 0; i < tuids.Count; i++)
        {
            _sections.Add(tuids[i], sections[i]);
        }           
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        List<Tuid> tuids = new List<Tuid>();
        List<Section> sections = new List<Section>();

        foreach (KeyValuePair<Tuid, Section> kvp in _sections)
        {
            tuids.Add(kvp.Key);
            sections.Add(kvp.Value);
        }

        info.AddValue("Sections_Keys", tuids, typeof(List<Tuid>));
        info.AddValue("Sections_Values", sections, typeof(List<Section>));
   }

The strategy here is to "unpack" the dictionary into two seperate lists and store them individually in the serialized stream. Then they are re-created afterwards.

My Section class also implments ISerializable...

[Serializable]
public class Section : BaseObject
{

    protected Section(SerializationInfo info, StreamingContext context):base(.....)
    {
        // Code
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
       // code
    }
}

The problem is that when I serialize GetObjectData() is called on both my Template and my Section which makes me believe that the data is Serializable and that its getting serialized.

When I deserialize, only the deserialize constructor on Template is called. The deserialize constructor for Section is never called. The result of this is that the call to info.GetValue("Section_Values"....) does return a List but it has one item in it and that item is null.

Why does my constructor to deserialize a Section never get called? Could it be that some of the data inside the section is not serializable? If so, how to find out what exactly it cannot serialize?

Update: One thing I have just spotted is that the BaseObject for section is marked with [Serializable] but does not implement ISerializable.

Additionally, Im wondering how fussy the Deserialize code is - will it target a constructor that also constructs a base class?

Update..

Ok, Ive tracked down the problem to the Serialization of the Section. The code looks something like this...

protected Section(SerializationInfo info, StreamingContext context):base(.....)
{
    // Code
}

public void GetObjectData(SerializationInfo info, StreamingContext context)
{

    //info.AddValue("CustomObject", ClientInfo, typeof(CustomObject));
    //info.AddValue("Description", Description, typeof(string));
}

With both of the lines commented out, nothing is serialized and the deserialization constructor is called on Section. If I add in the string value everything is still fine. However, yes - you guessed it - if I add the CustomObject into the serialization stream then the deserialization constructor is not called.

Note that...

  • My deserialization constructor for Section is a blank method - I dont attempt to do anything with the deserialized data.
  • The base constructor for Section has been stubbed out to pass in new valid objects and I have confirmed that this runs fine.
  • No exceptions are thrown to tell me that the CustomObject cannot be serialized.
  • The CustomObject is serializable and its GetObjectData() method runs fine and its constructed fine on deserialization.

It seems strange that purely be adding this serializable object to the stream that the framework then just fails to the deserializer constructor of Section!!

Why could this possibly be happening?

like image 448
Remotec Avatar asked Dec 01 '11 10:12

Remotec


People also ask

Why constructor is not called during deserialization?

The deserialization process does not use the object's constructor - the object is instantiated without a constructor and initialized using the serialized instance data.

What is valid about constructor call during deserialization process in Java?

What Is valid about constructor call during DeSerialization process in java? b. if Externalizable has been implemented - constructor is called during DeSerialization process.

What is the difference between deserialization and serialization?

Serialization is a mechanism of converting the state of an object into a byte stream. Deserialization is the reverse process where the byte stream is used to recreate the actual Java object in memory. This mechanism is used to persist the object. The byte stream created is platform independent.

Which method is used for deserialization?

For serializing the object, we call the writeObject() method of ObjectOutputStream class, and for deserialization we call the readObject() method of ObjectInputStream class. We must have to implement the Serializable interface for serializing the object.

Does deserialization use the object's constructor?

The deserialization process does not use the object's constructor - the object is instantiated without a constructor and initialized using the serialized instance data.

What constructors are ignored by the serializer?

Non-public constructors, including parameterless constructors, are ignored by the serializer by default. The serializer uses one of the following constructors for deserialization: Public constructor annotated with JsonConstructorAttribute. Public parameterless constructor.

How do I deserialize a JSON object?

Implement deserialization by doing the following steps in the Read () method: Parse JSON into a JsonDocument. Get the properties needed to call the parameterized constructor. Create the object with the parameterized constructor. Set the rest of the properties.

Why doesn't jsonserializer call the non-public surface area of a type?

Because JsonSerializer shouldn't call the non-public surface area of a type, whether that's a constructor, a property, or a field. If you own the type and it's feasible, make the parameterless constructor public. Otherwise, implement a JsonConverter<T> for the type and control the deserialization behavior.


1 Answers

One of the options is to implement

[OnDeserializing]
void OnDeserializing(StreamingContext c)
{
    //create what is required here
}

in theTemplate class, as the default serrializer does not call the constructor for child objects - Section class

like image 188
oleksii Avatar answered Oct 10 '22 11:10

oleksii