Details of the Serializable serialization instance in C

  • 2020-11-26 18:58:01
  • OfStack

This article provides an example of Serializable serialization in C#. Share to everybody for everybody reference. The specific analysis is as follows:

Summary:

Serialization is the process of converting an object into a format that is easy to transfer, usually a stream file, into memory or an IO file. For example, you can serialize an object and then use HTTP to transfer the object between the client and server over Internet, or share it with other applications. Conversely, deserialization reconstructs the object according to the flow.

1. Several serialization techniques

1) Base 2 serialization maintains type fidelity, which is useful for preserving the state of an object between calls to the application. For example, objects can be Shared between different applications by serializing them to the clipboard. You can serialize objects to streams, disks, memory, networks, and so on. Remote processing USES serialization "by value" to pass objects between computers or application domains.

2) XML serialization serializes only public properties and fields, and does not maintain type fidelity. This is useful when you want to provide or use data without restricting the applications that use it. Since XML is an open standard, this is a good choice for sharing data through Web. SOAP is also an open standard, which makes it an attractive option.

3) Serialize and deserialize type instances into XML streams or documents (or JSON format) using the data protocols provided. It is often used in WCF communication.

2. Serialization classification

1. Basic serialization

The easiest way to make a class serializable is to tag it with the Serializable attribute, as shown below

[Serializable]
public class MyObject
{
   public int n1 = 0;
   public int n2 = 0;
   public String str = null;
}

Serialize 1 instance of the above class into 1 file

MyObject obj = new MyObject();
obj.n1 = 1;
obj.n2 = 24;
obj.str = "1 Some of the string ";
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("MyFile.bin", FileMode.Create,
FileAccess.Write, FileShare.None);
formatter.Serialize(stream, obj);
stream.Close();


Deserialization of the above instance

IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("MyFile . bin", FileMode.Open,
FileAccess.Read, FileShare.Read);
MyObject obj = (MyObject) formatter.Deserialize(fromStream);
stream.Close();

If portability is required, use SoapFormatter. All you need to do is replace the formatter in the above code with SoapFormatter, leaving the Serialize and Deserialize calls unchanged.

Note that the Serializable attribute cannot be inherited. If a new class is derived from MyObject, the new class must also be tagged with this attribute, otherwise it will not be serialized. For example, if you try to serialize an instance of the following class, an SerializationException will be displayed, indicating that the MyStuff type is not marked as serializable.

2. Selective serialization

Classes usually contain fields that should not be serialized. For example, suppose a class USES a member variable to store the thread ID. When this class is deserialized, the thread corresponding to ID that was stored when the class was serialized may no longer run, so serializing this value makes no sense. You can prevent member variables from being serialized by marking them with the NonSerialized attribute, as shown below:

[Serializable]
public class MyObject
{
   public int n1;
   [NonSerialized]
   public int n2;
   public String str;
}

3. Custom serialization

You can customize the serialization process by implementing the ISerializable interface on an object. This 1 feature is especially useful when the value of a member variable is invalidated after deserialization, but you need to provide a value for the variable to reconstruct the object's full state. To implement ISerializable, you need to implement the GetObjectData method and a special constructor that is used when deserializing objects. The following code example shows how to implement ISerializable on the MyObject class mentioned in the previous section.

[Serializable]
public class MyObject : ISerializable
{
   public int n1;
   public int n2;
   public String str;
   public MyObject()
   {
   }
   protected MyObject(SerializationInfo info, StreamingContext context)
   {
     n1 = info.GetInt32("i");
     n2 = info.GetInt32("j");
     str = info.GetString("k");
   }
   public virtual void GetObjectData(SerializationInfo info,
StreamingContext context)
   {
     info.AddValue("i", n1);
     info.AddValue("j", n2);
     info.AddValue("k", str);
   }
}


When GetObjectData is called during serialization, the SerializationInfo object provided in the method call needs to be populated. Simply add the variable to be serialized in the form of a name/value pair. Its name can be any text. As long as the serialized data is sufficient to restore the object during deserialization, you are free to select the member variables added to SerializationInfo. If the base object implements ISerializable, the derived class should call the GetObjectData method of its base object.

It is important to note that when you add ISerializable to a class, you need to implement both GetObjectData and the special constructor. If GetObjectData is missing, the compiler will issue a warning. However, because constructors cannot be enforced, no warnings are issued when constructors are missing. If you try to deserialize a class without a constructor, an exception will occur. The current design is superior to the SetObjectData approach in eliminating potential security and versioning issues. For example, if the SetObjectData method is defined as part 1 of an interface, then this method must be public, forcing the user to write code to prevent multiple calls to the SetObjectData method. As you can imagine, if an object is doing something and a malicious application calls the object's SetObjectData method, it can cause some potential trouble.

During the deserialization process, SerializationInfo is passed to the class using the constructor provided for this purpose. When the object is deserialized, any visibility constraints on the constructor are ignored, so you can mark the class as public, protected, internal, or private. A good idea is to mark the constructor protect if the class is not encapsulated. If the class is encapsulated, it should be marked as private. To restore the state of the object, simply retrieve the value of the variable from SerializationInfo using the name you used when you serialized it. If the base class implements ISerializable, the base class's constructor should be called so that the base object can restore its variables.

If you derive a new class from a class that implements ISerializable, you must implement both the constructor and the GetObjectData method as long as the new class contains any variables that need to be serialized. The following code snippet shows how to do this using the MyObject class shown above.

[Serializable]
public class ObjectTwo : MyObject
{
   public int num;
   public ObjectTwo() : base()
   {
   }
   protected ObjectTwo(SerializationInfo si, StreamingContext context) :
base(si,context)
   {
     num = si.GetInt32("num");
   }
   public override void GetObjectData(SerializationInfo si,
StreamingContext context)
   {
     base.GetObjectData(si,context);
     si.AddValue("num", num);
   }
}

Remember to call the base class in the deserialization constructor; otherwise, the constructor on the base class will never be called, and the full object will not be built after deserialization. Retrieving keyword/value pairs during deserialization is easy, but there is no guarantee that the classes derived from the hash are deserialized, so adding these objects back to the hash table can be a problem. Therefore, it is recommended not to call methods on the hash table for now.

3. Methods if the state of an object needs to change between versions

1. ISerializable This gives you precise control over the serialization and deserialization process, where future states are correctly added and interpreted.

2. Use the NonSerialized attribute to mark unimportant member variables. Use this option only if you expect the class to change less from version to version. For example, after adding a new variable to an older version of a class, you can mark it as NonSerialized to ensure that the class remains compatible with earlier versions.

Hopefully, this article has helped you with your C# programming.


Related articles: