Home

Serialization

 

Object Serialization and De-Serialization

 

Introduction

Consider the following program:

Serialization

#using <System.dll>
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>

using namespace System;
using namespace System::IO;
using namespace System::Drawing;
using namespace System::Windows::Forms;

public ref class CExercise : public Form
{
private:
	Label    ^ lblMake;
	TextBox  ^ txtMake;
	Label    ^ lblModel;
	TextBox  ^ txtModel;
	Label    ^ lblYear;
	TextBox  ^ txtYear;
	Label    ^ lblColor;
	ComboBox ^ cbxColors;
	Button   ^ btnWrite;

public:
	CExercise()
	{
		Text = L"Car Review";
		Size = System::Drawing::Size(220, 170);

		lblMake = gcnew Label;
		lblMake->Location = Point(10, 14);
		lblMake->AutoSize = true;
		lblMake->Text = L"Make:";
		txtMake = gcnew TextBox;
		txtMake->Location = Point(80, 10);
		
		lblModel = gcnew Label;
		lblModel->Location = Point(10, 38);
		lblModel->AutoSize = true;
		lblModel->Text = L"Model:";
		txtModel = gcnew TextBox;
		txtModel->Location = Point(80, 34);
		
		lblYear = gcnew Label;
		lblYear->Location = Point(10, 62);
		lblYear->AutoSize = true;
		lblYear->Text = L"Year:";
		txtYear = gcnew TextBox;
		txtYear->Location = Point(80, 58);
		
	        lblColor = gcnew Label;
		lblColor->Location = Point(10, 84);
		lblColor->AutoSize = true;
		lblColor->Text = L"Year:";
		cbxColors = gcnew ComboBox;
		cbxColors->Location = Point(80, 82);
		cbxColors->Items->Add(L"Black");
		cbxColors->Items->Add(L"White");
		cbxColors->Items->Add(L"Gray");
		cbxColors->Items->Add(L"Silver");
		cbxColors->Items->Add(L"Blue");

		btnWrite = gcnew Button;
		btnWrite->Location = Point(10, 112);
		btnWrite->Text = L"Write";
		btnWrite->Click += gcnew EventHandler(this, &CExercise::btnWriteClick);

		Controls->Add(lblMake);
		Controls->Add(txtMake);
		Controls->Add(lblModel);
		Controls->Add(txtModel);
		Controls->Add(lblYear);
		Controls->Add(txtYear);
		Controls->Add(lblColor);
		Controls->Add(cbxColors);
		Controls->Add(btnWrite);
	}
	
	void btnWriteClick(Object ^ sender, EventArgs ^ e)
	{
		String ^ Make  = txtMake->Text;
		String ^ Model = txtModel->Text;
		int Year  = int::Parse(txtYear->Text);
		int Color = cbxColors->SelectedIndex;

		FileStream ^ stmCar = gcnew FileStream(L"Car1.car", FileMode::Create);
		BinaryWriter ^ bnwCar = gcnew BinaryWriter(stmCar);

		try
		{
			bnwCar->Write(Make);
			bnwCar->Write(Model);
			bnwCar->Write(Year);
			bnwCar->Write(Color);
		}
		finally
		{
			bnwCar->Close();
			stmCar->Close();
		}
	}
};

int main()
{
	Application::Run(gcnew CExercise);
	return 0;
}

Here is an example of running the program:

This is an example of the techniques used in file processing to save individual data of primitive types:

Saving the variables in a method

The values can be retrieved with the following code:

#using <System.dll>
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>

using namespace System;
using namespace System::IO;
using namespace System::Drawing;
using namespace System::Windows::Forms;

public ref class CExercise : public Form
{
private:
	Label    ^ lblMake;
	TextBox  ^ txtMake;
	Label    ^ lblModel;
	TextBox  ^ txtModel;
	Label    ^ lblYear;
	TextBox  ^ txtYear;
	Label    ^ lblColor;
	ComboBox ^ cbxColors;
	Button   ^ btnWrite;
	Button   ^ btnRead;

public:
	CExercise()
	{
		Text = L"Car Review";
		Size = System::Drawing::Size(220, 170);

		lblMake = gcnew Label;
		lblMake->Location = Point(10, 14);
		lblMake->AutoSize = true;
		lblMake->Text = L"Make:";
		txtMake = gcnew TextBox;
		txtMake->Location = Point(80, 10);
		
		lblModel = gcnew Label;
		lblModel->Location = Point(10, 38);
		lblModel->AutoSize = true;
		lblModel->Text = L"Model:";
		txtModel = gcnew TextBox;
		txtModel->Location = Point(80, 34);
		
		lblYear = gcnew Label;
		lblYear->Location = Point(10, 62);
		lblYear->AutoSize = true;
		lblYear->Text = L"Year:";
		txtYear = gcnew TextBox;
		txtYear->Location = Point(80, 58);
		
	        lblColor = gcnew Label;
		lblColor->Location = Point(10, 84);
		lblColor->AutoSize = true;
		lblColor->Text = L"Year:";
		cbxColors = gcnew ComboBox;
		cbxColors->Location = Point(80, 82);
		cbxColors->Items->Add(L"Black");
		cbxColors->Items->Add(L"White");
		cbxColors->Items->Add(L"Gray");
		cbxColors->Items->Add(L"Silver");
		cbxColors->Items->Add(L"Blue");

		btnWrite = gcnew Button;
		btnWrite->Location = Point(10, 112);
		btnWrite->Text = L"Write";
		btnWrite->Click += gcnew EventHandler(this, &CExercise::btnWriteClick);

		btnRead = gcnew Button;
		btnRead->Location = Point(120, 112);
		btnRead->Text = L"Read";
		btnRead->Click += gcnew EventHandler(this, &CExercise::btnReadClick);

		Controls->Add(lblMake);
		Controls->Add(txtMake);
		Controls->Add(lblModel);
		Controls->Add(txtModel);
		Controls->Add(lblYear);
		Controls->Add(txtYear);
		Controls->Add(lblColor);
		Controls->Add(cbxColors);
		Controls->Add(btnWrite);
		Controls->Add(btnRead);
	}
	
	void btnWriteClick(Object ^ sender, EventArgs ^ e)
	{
		String ^ Make  = txtMake->Text;
		String ^ Model = txtModel->Text;
		int Year  = int::Parse(txtYear->Text);
		int Color = cbxColors->SelectedIndex;

		FileStream ^ stmCar = gcnew FileStream(L"Car1.car", FileMode::Create);
		BinaryWriter ^ bnwCar = gcnew BinaryWriter(stmCar);

		try
		{
			bnwCar->Write(Make);
			bnwCar->Write(Model);
			bnwCar->Write(Year);
			bnwCar->Write(Color);
		}
		finally
		{
			bnwCar->Close();
			stmCar->Close();
		}
	}
	
	void btnReadClick(Object ^ sender, EventArgs ^ e)
        {
		FileStream ^ stmCar = gcnew FileStream(L"Car1.car", FileMode::Open);
		BinaryReader ^ bnrCar = gcnew BinaryReader(stmCar);

		try
		{
			txtMake->Text = bnrCar->ReadString();
			txtModel->Text = bnrCar->ReadString();
			txtYear->Text = bnrCar->ReadUInt32().ToString();
			cbxColors->SelectedIndex = bnrCar->ReadInt32();
		}
		finally
		{
			bnrCar->Close();
			stmCar->Close();
		}
	}
};

int main()
{
	Application::Run(gcnew CExercise);
	return 0;
}

In the same way, you can save the individual fields of a class or you can retrieve the individual fields of a car:

Saving the individual parts of an object

Here is an example:

Header File: Car.h

#pragma once
using namespace System;

ref class CCar
{
public:
    CCar(void);

    String ^ Make;
    String ^ Model;
    int Year;
    int Color;
};

Source File: Car.cpp

#include "Car.h"

CCar::CCar(void)
    : Make(L"Unknown"), Model(L"Unknown"), Year(1960), Color(0)
{
}

Source File: Exercise.cpp

#pragma once
#include "Car.h"

#using <System.dll>
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>

using namespace System;
using namespace System::IO;
using namespace System::Drawing;
using namespace System::Windows::Forms;

public ref class CExercise : public Form
{
private:
	Label    ^ lblMake;
	TextBox  ^ txtMake;
	Label    ^ lblModel;
	TextBox  ^ txtModel;
	Label    ^ lblYear;
	TextBox  ^ txtYear;
	Label    ^ lblColor;
	ComboBox ^ cbxColors;
	Button   ^ btnWrite;
	Button   ^ btnRead;

public:
	CExercise()
	{
		Text = L"Car Review";
		Size = System::Drawing::Size(220, 170);

		lblMake = gcnew Label;
		lblMake->Location = Point(10, 14);
		lblMake->AutoSize = true;
		lblMake->Text = L"Make:";
		txtMake = gcnew TextBox;
		txtMake->Location = Point(80, 10);
		
		lblModel = gcnew Label;
		lblModel->Location = Point(10, 38);
		lblModel->AutoSize = true;
		lblModel->Text = L"Model:";
		txtModel = gcnew TextBox;
		txtModel->Location = Point(80, 34);
		
		lblYear = gcnew Label;
		lblYear->Location = Point(10, 62);
		lblYear->AutoSize = true;
		lblYear->Text = L"Year:";
		txtYear = gcnew TextBox;
		txtYear->Location = Point(80, 58);
		
	        lblColor = gcnew Label;
		lblColor->Location = Point(10, 84);
		lblColor->AutoSize = true;
		lblColor->Text = L"Year:";
		cbxColors = gcnew ComboBox;
		cbxColors->Location = Point(80, 82);
		cbxColors->Items->Add(L"Black");
		cbxColors->Items->Add(L"White");
		cbxColors->Items->Add(L"Gray");
		cbxColors->Items->Add(L"Silver");
		cbxColors->Items->Add(L"Blue");

		btnWrite = gcnew Button;
		btnWrite->Location = Point(10, 112);
		btnWrite->Text = L"Write";
		btnWrite->Click += gcnew EventHandler(this, &CExercise::btnWriteClick);

		btnRead = gcnew Button;
		btnRead->Location = Point(120, 112);
		btnRead->Text = L"Read";
		btnRead->Click += gcnew EventHandler(this, &CExercise::btnReadClick);

		Controls->Add(lblMake);
		Controls->Add(txtMake);
		Controls->Add(lblModel);
		Controls->Add(txtModel);
		Controls->Add(lblYear);
		Controls->Add(txtYear);
		Controls->Add(lblColor);
		Controls->Add(cbxColors);
		Controls->Add(btnWrite);
		Controls->Add(btnRead);
	}
	
	void btnWriteClick(Object ^ sender, EventArgs ^ e)
	{
		CCar ^ vehicle = gcnew CCar;           
		vehicle->Make  = txtMake->Text;
		vehicle->Model = txtModel->Text;
		vehicle->Year  = int::Parse(txtYear->Text);
		vehicle->Color = cbxColors->SelectedIndex;

		FileStream ^ stmCar = gcnew FileStream(L"Car2.car", FileMode::Create);
		BinaryWriter ^ bnwCar = gcnew BinaryWriter(stmCar);

		try
		{
			bnwCar->Write(vehicle->Make);
			bnwCar->Write(vehicle->Model);
			bnwCar->Write(vehicle->Year);
			bnwCar->Write(vehicle->Color);
		}
		finally
		{
			bnwCar->Close();
			stmCar->Close();
		}
	}
	
	void btnReadClick(Object ^ sender, EventArgs ^ e)
        {
		FileStream ^ stmCar = gcnew FileStream(L"Car2.car", FileMode::Open);
		BinaryReader ^ bnrCar = gcnew BinaryReader(stmCar);

		try
		{
			CCar ^ vehicle = gcnew CCar;
			vehicle->Make = bnrCar->ReadString();
			vehicle->Model = bnrCar->ReadString();
			vehicle->Year = bnrCar->ReadUInt32();
			vehicle->Color = bnrCar->ReadInt32();

			txtMake->Text = vehicle->Make;
			txtModel->Text = vehicle->Model;
			txtYear->Text = vehicle->Year.ToString();
			cbxColors->SelectedIndex = vehicle->Color;
		}
		finally
		{
			bnrCar->Close();
			stmCar->Close();
		}
	}
};

int main()
{
	Application::Run(gcnew CExercise);
	return 0;
}

When it comes to a class, the problem with saving individual fields is that you could forget to save one of the fields. For example, considering a CCar class, if you don't save the Make information of a CCar object and retrieve or open the saved object on another computer, the receiving user would miss some information and the car cannot be completely identifiable. An alternative is to save the whole Car object.

Object serialization consists of saving a whole object as one instead of its individual fields:

Serialization

In other words, a variable declared from a class can be saved to a stream and then the saved object can be retrieved later or on another computer. The .NET Framework supports two types of object serialization: binary and SOAP.

Practical Learning: Introducing Serialization

  1. Start Microsoft Visual Studio
  2. Create a Windows Application named RealEstate1
  3. Design the form as follows:
     
    Altair Realtors
    Control Text Name
    Label Label Property #:  
    TextBox TextBox   txtPropertyNumber
    Button Open btnOpen
    Label Label Property Type:  
    TextBox TextBox   txtPropertyType
    Label Label Address:  
    TextBox TextBox   txtAddress
    Label Label City:  
    TextBox TextBox   txtCity
    Label Label State:  
    TextBox TextBox   txtState
    Label Label ZIP Code:  
    TextBox TextBox   txtZIPCode
    Label Label Bedrooms:  
    TextBox TextBox 0 txtBedrooms
    Label Label Bathrooms:  
    TextBox TextBox 1.0 txtBathrooms
    Label Label Market Value:  
    TextBox TextBox 0.00 txtMarketValue
    Button Button &New Property... btnNewProperty
    Button Button Close btnClose
    Form
    FormBorderStyle: FixedDialog
    Text: Altair Realtors - Property Editor
    StartPosition: CenterScreen
  4. To create a new form, on the main menu, click Projects -> Add New Item...
  5. In the Templates list, click Windows Form
  6. Set the Name to PropertyEditor and click Add
  7. Design the form as follows:
     
    Real Estate
    Control Text Name Other Properties
    Label Label Property #:    
    TextBox TextBox   txtPropertyNumber Modifiers: Public
    Label Label Property Type:    
    ComboBox ComboBox   cbxPropertyTypes Modifiers: Public
    Items:
    Unknown
    Single Family
    Townhouse
    Condominium
    Label Label Address:    
    TextBox TextBox   txtAddress Modifiers: Public
    Label Label City:    
    TextBox TextBox   txtCity Modifiers: Public
    Label Label State:    
    ComboBox TextBox   cbxStates Modifiers: Public
    Items:
    DC
    MD
    PA
    VA
    WV
    Label Label ZIP Code:    
    TextBox TextBox   txtZIPCode Modifiers: Public
    Label Label Bedrooms:    
    TextBox TextBox 0 txtBedrooms Modifiers: Public
    Label Label Bathrooms:    
    TextBox TextBox 1.0 txtBathrooms Modifiers: Public
    Label Label Market Value:    
    TextBox TextBox 0.00 txtMarketValue Modifiers: Public
    Button Button OK btnOK DialogResult: OK
    Button Button Cancel btnCancel DialogResult: Cancel
    Form
    FormBorderStyle: FixedDialog
    Text: Altair Realtors - Property Editor
    StartPosition: CenterScreen
    AcceptButton: btnOK
    CancelButton: btnCancel
    MaximizeBox: False
    MinimizeBox: False
    ShowInTaskBar: False
  8. Save the form

Serialization

Binary serialization works by processing an object rather than streaming its individual member variables. This means that, to use it, you define an object and initialize it, or "fill" it, with the necessary values and any information you judge necessary. This creates a "state" of the object. It is this state that you prepare to serialize. When you save the object, it is converted into a stream.

To perform binary serialization, there are a few steps you must follow. When creating the class whose objects would be serialized, start it with the [Serializable] attribute. Here is an example:

#pragma once
using namespace System;

[Serializable]
ref class CCar
{
public:
    CCar(void);

    String ^ Make;
    String ^ Model;
    int Year;
    int Color;
};

Before serializing an object, you should reference the System::Runtime::Serialization::Formatters::Binary namespace. The class responsible for binary serialization is called BinaryFormatter. This class is equipped with two constructors. The default constructor is used to simply create an object.

After declaring the variable, to actually serialize an object, call the Serialize() method of the BinaryFormatter class. The method is overloaded with two versions. One of the versions of this method uses the following syntax:

public:
    virtual void Serialize(Stream ^ serializationStream,
			   Object ^ graph) sealed;

The first argument to this method must be an object of a Stream-based class, such as a FileStream object. The second argument must be the object to serialize. This means that, before calling this method, you should have built the object.

Here is an example:

#pragma once
#include "Car.h"

#using <System.dll>
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>

using namespace System;
using namespace System::IO;
using namespace System::Drawing;
using namespace System::Windows::Forms;
using namespace System::Runtime::Serialization::Formatters::Binary;

public ref class CExercise : public Form
{
private:
    Label    ^ lblMake;
    TextBox  ^ txtMake;
    Label    ^ lblModel;
    TextBox  ^ txtModel;
    Label    ^ lblYear;
    TextBox  ^ txtYear;
    Label    ^ lblColor;
    ComboBox ^ cbxColors;
    Button   ^ btnWrite;
    Button   ^ btnRead;

public:
    CExercise()
    {
	. . . No Change
    }
	
    void btnWriteClick(Object ^ sender, EventArgs ^ e)
    {
	CCar ^ vehicle = gcnew CCar;
        vehicle->Make  = txtMake->Text;
        vehicle->Model = txtModel->Text;
        vehicle->Year  = int::Parse(txtYear->Text);
	vehicle->Color = cbxColors->SelectedIndex;

	FileStream ^ stmCar = gcnew FileStream(L"Car3.car",
                                               FileMode::Create);
	BinaryFormatter ^ bfmCar = gcnew BinaryFormatter;
	bfmCar->Serialize(stmCar, vehicle);
    }
};

int main()
{
    Application::Run(gcnew CExercise);
    return 0;
}

Practical Learning: Serializing an Object

  1. To create a new class, on the main menu, click Project -> Add Class...
  2. Select C++ Class and click Add
  3. Set the Name to CRealEstateProperty and click Add
  4. Change the file as follows:
     
    #pragma once
    using namespace System;
    
    [Serializable]
    public ref class CRealEstateProperty
    {
    public:
        CRealEstateProperty(void);
    
        String ^ PropertyNumber;
        String ^ PropertyType;
        String ^ Address;
        String ^ City;
        String ^ State;
        int ZIPCode;
        short Bedrooms;
        float Bathrooms;
        double MarketValue;
    };
  5. On the main menu, click Windows -> Form1.h [Design]
  6. On the form, double-click New Property...
  7. In the top section of the file, include the PropertyEditor.h header file, the RealEstateProperty.h header file, the System::IO namespace, the System::Runtime::Serialization::Formatters::Binary namespace:
     
    #pragma once
    #include "PropertyEditor.h"
    #include "RealEstateProperty.h"
    
    namespace RealEstate1 {
    
    	using namespace System;
    	using namespace System::ComponentModel;
    	using namespace System::Collections;
    	using namespace System::Windows::Forms;
    	using namespace System::Data;
    	using namespace System::Drawing;
    	using namespace System::IO;
    using namespace System::Runtime::Serialization::Formatters::Binary;
  8. Implement the event as follows:
     
    System::Void btnNewProperty_Click(System::Object^  sender, System::EventArgs^  e)
    {
        PropertyEditor ^ editor = gcnew PropertyEditor();
        DirectoryInfo ^ dirInfo =
        Directory::CreateDirectory(L"C:\\Altair Realtors\\Properties");
    
        Random ^ rnd = gcnew Random;
        int left = rnd->Next(100, 999);
        int right = rnd->Next(100, 999);
        editor->txtPropertyNumber->Text = left.ToString()+ L"-"
                                        + right.ToString();
    
        if (editor->ShowDialog() == System::Windows::Forms::DialogResult::OK)
        {
            CRealEstateProperty ^ prop = gcnew CRealEstateProperty;
            prop->PropertyNumber = editor->txtPropertyNumber->Text;
            prop->PropertyType = editor->cbxPropertyTypes->Text;
            prop->Address = editor->txtAddress->Text;
            prop->City = editor->txtCity->Text;
            prop->State = editor->cbxStates->Text;
            prop->ZIPCode = int::Parse(editor->txtZIPCode->Text);
    		prop->Bedrooms = short::Parse(editor->txtBedrooms->Text);
    		prop->Bathrooms = float::Parse(editor->txtBathrooms->Text);
            prop->MarketValue = double::Parse(editor->txtMarketValue->Text);
    
            String ^ strFilename = dirInfo->FullName+ L"\\" +
                        editor->txtPropertyNumber->Text+ L".prp";
            FileStream ^ stmProperty = gcnew FileStream(strFilename,
                        FileMode::Create, FileAccess::Write);
            BinaryFormatter ^ bfmProperty = gcnew BinaryFormatter;
    
            bfmProperty->Serialize(stmProperty, prop);
        }
    }
  9. Return to the form and double-click Close
  10. Implement the event as follows:
     
    System::Void btnClose_Click(System::Object^  sender, System::EventArgs^  e)
    {
        Close();
    }
  11. Execute the application and continuously click the New Property button to create the following properties (let the computer specify the property number):
     
    Real Estate
     
    Property Type  Address  City State  ZIP Code Beds Baths Market Value
    Single Family 11604 Aldora Avenue Baltimore MD 21205 5 3.5 325650
    Townhouse 495 Parker House Terrace Gettysburg WV 26201 3 2.5 225500
    Condominium 5900 24th Street NW #812 Washington DC 20008 1 1.0 388665
    Single Family 6114 Costinha Avenue Martinsburg WV 25401 4 3.5 325000
    Condominium 10710 Desprello Street #10D Rockville MD 20856 1 1.0 528445
  12. Close the form and return to your programming environment

De-Serialization

As serialization is the process of storing an object to a medium, the opposite, serialization is used to retrieve an object from a stream. To support this, the BinaryFormatter class is equipped with the Deserialize() method. Like Serialize(), the Deserialize() method is overloaded with two versions. One of them uses the following syntax:

public:
    Object ^ Deserialize(Stream ^ serializationStream) sealed;

This method takes as argument a Stream-based object, such as a FileStream variable, that indicates where the file is located. The Deserialize() method returns an Object object. As a goal, you want the Deserialize() method to produce the type of object that was saved so you can retrieve the values that the returned object holds. Because the method returns an Object value, you must cast the returned value to the type of your class.

Once the Deserialize() method has returned the desired object, you can access its values. Here is an example:

#pragma once
#include "Car.h"

#using <System.dll>
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>

using namespace System;
using namespace System::IO;
using namespace System::Drawing;
using namespace System::Windows::Forms;
using namespace System::Runtime::Serialization::Formatters::Binary;

public ref class CExercise : public Form
{
private:
	Label    ^ lblMake;
	TextBox  ^ txtMake;
	Label    ^ lblModel;
	TextBox  ^ txtModel;
	Label    ^ lblYear;
	TextBox  ^ txtYear;
	Label    ^ lblColor;
	ComboBox ^ cbxColors;
	Button   ^ btnWrite;
	Button   ^ btnRead;

public:
	CExercise()
	{
		Text = L"Car Review";
		Size = System::Drawing::Size(220, 170);

		lblMake = gcnew Label;
		lblMake->Location = Point(10, 14);
		lblMake->AutoSize = true;
		lblMake->Text = L"Make:";
		txtMake = gcnew TextBox;
		txtMake->Location = Point(80, 10);
		
		lblModel = gcnew Label;
		lblModel->Location = Point(10, 38);
		lblModel->AutoSize = true;
		lblModel->Text = L"Model:";
		txtModel = gcnew TextBox;
		txtModel->Location = Point(80, 34);
		
		lblYear = gcnew Label;
		lblYear->Location = Point(10, 62);
		lblYear->AutoSize = true;
		lblYear->Text = L"Year:";
		txtYear = gcnew TextBox;
		txtYear->Location = Point(80, 58);
		
	    lblColor = gcnew Label;
		lblColor->Location = Point(10, 84);
		lblColor->AutoSize = true;
		lblColor->Text = L"Year:";
		cbxColors = gcnew ComboBox;
		cbxColors->Location = Point(80, 82);
		cbxColors->Items->Add(L"Black");
		cbxColors->Items->Add(L"White");
		cbxColors->Items->Add(L"Gray");
		cbxColors->Items->Add(L"Silver");
		cbxColors->Items->Add(L"Blue");

		btnWrite = gcnew Button;
		btnWrite->Location = Point(10, 112);
		btnWrite->Text = L"Write";
		btnWrite->Click += gcnew EventHandler(this, &CExercise::btnWriteClick);

		btnRead = gcnew Button;
		btnRead->Location = Point(120, 112);
		btnRead->Text = L"Read";
		btnRead->Click += gcnew EventHandler(this, &CExercise::btnReadClick);

		Controls->Add(lblMake);
		Controls->Add(txtMake);
		Controls->Add(lblModel);
		Controls->Add(txtModel);
		Controls->Add(lblYear);
		Controls->Add(txtYear);
		Controls->Add(lblColor);
		Controls->Add(cbxColors);
		Controls->Add(btnWrite);
		Controls->Add(btnRead);
	}
	
    void btnWriteClick(Object ^ sender, EventArgs ^ e)
    {
	CCar ^ vehicle = gcnew CCar;
        vehicle->Make  = txtMake->Text;
        vehicle->Model = txtModel->Text;
        vehicle->Year  = int::Parse(txtYear->Text);
	vehicle->Color = cbxColors->SelectedIndex;

	FileStream ^ stmCar = gcnew FileStream(L"Car3.car",
                                               FileMode::Create);
	BinaryFormatter ^ bfmCar = gcnew BinaryFormatter;
	bfmCar->Serialize(stmCar, vehicle);
    }
	
    void btnReadClick(Object ^ sender, EventArgs ^ e)
    {
	FileStream ^ stmCar = gcnew FileStream(L"Car3.car",
                                              FileMode::Open);
	BinaryFormatter ^ bfmCar = gcnew BinaryFormatter;
        CCar ^ vehicle = dynamic_cast<CCar ^>(bfmCar->Deserialize(stmCar));

        txtMake->Text = vehicle->Make;
        txtModel->Text = vehicle->Model;
        txtYear->Text = vehicle->Year.ToString();
        cbxColors->SelectedIndex = vehicle->Color;
    }
};

int main()
{
	Application::Run(gcnew CExercise);
	return 0;
}

Practical Learning: De-Serializing an Object

  1. On the (first) form, double-click the Open button and implement its event as follows: 
    System::Void btnOpen_Click(System::Object^  sender, System::EventArgs^  e)
    {
        DirectoryInfo ^ dirProperties =
    		 gcnew DirectoryInfo(L"C:\\Altair Realtors\\Properties");
        array<FileInfo ^> ^ fleProperties = dirProperties->GetFiles();
    
        if (dirProperties->Exists == true)
        {
            bool found = false;
            CRealEstateProperty ^ prop = nullptr;
    
            for each( FileInfo ^ fle in fleProperties)
            {
                FileStream ^ stmProperty = gcnew FileStream(fle->FullName,
                                                        FileMode::Open,
    						    FileAccess::Read);
                BinaryFormatter ^ bfmProperty = gcnew BinaryFormatter;
                prop = dynamic_cast<CRealEstateProperty ^>(bfmProperty->Deserialize(stmProperty));
    
                if (prop->PropertyNumber == txtPropertyNumber->Text)
                {
                    found = true;
                }
            }
    
            if (found == true)
            {
                txtPropertyType->Text = prop->PropertyType;
                txtAddress->Text = prop->Address;
                txtCity->Text = prop->City;
                txtState->Text = prop->State;
                txtZIPCode->Text = prop->ZIPCode.ToString();
                txtBedrooms->Text = prop->Bedrooms.ToString();
                txtBathrooms->Text = prop->Bathrooms.ToString(L"F");
                txtMarketValue->Text = prop->MarketValue.ToString(L"F");
            }
            else
            {
                MessageBox::Show(L"There is no property with " 
    			    L"that number in our database");
    
                txtPropertyType->Text = L"Unknown";
                txtAddress->Text = L"";
                txtCity->Text = L"";
                txtState->Text = L"";
                txtZIPCode->Text = L"00000";
                txtBedrooms->Text = L"0";
                txtBathrooms->Text = L"0.00";
                txtMarketValue->Text = L"0.00";
            }
        }
    }
  2. Execute the application and try opening a previously saved property using its number
  3. Close the form and return to your programming environment
 

 

 
 

SOAP Serialization

 

Introduction

The .NET Framework supports another technique of serialization referred to as SOAP (which stands for Simple Object Access Protocol). This technique is a related to XML but, although we haven't studied XML, you don't need to know anything about it to use SOAP serialization.

Practical Learning: Introducing SOAP Serialization

  1. Start a new Windows Application named WattsALoan3
  2. Design the form as followed:
     
    Watts A Loan
    Control Name Text Additional Properties
    GroupBox GroupBox   Loan Identification  
    Label Label   Prepared &By:  
    TextBox TextBox txtEmployeeName    
    Label Label   Customer First Name:  
    TextBox TextBox txtCustomerFirstName    
    Label Label   Last Name:  
    TextBox TextBox txtCustomerLastName    
    GroupBox GroupBox   Loan Preparation  
    Label Label   Principal:  
    TextBox TextBox txtPrincipal 0.00 TextAlign: Right
    Label Label   Interest Rate:  
    TextBox TextBox txtInterestRate 8.25 TextAlign: Right
    Label Label   %  
    Label Label   Periods:  
    TextBox TextBox txtPeriods 1 TextAlign: Text
    ComboBox cbxPeriods Months Items:
    Years
    Months
    Days
    GroupBox GroupBox   Results  
    Button Button btnCalculate Calculate  
    Label Label   Interest Earned:  
    TextBox TextBox txtInterestEarned 0.00 TextAlign: Right
    ReadOnly: True
    Label Label   Amount Earned:  
    TextBox TextBox txtFutureValue 0.00 TextAlign: Right
    ReadOnly: True
    GroupBox GroupBox   File Processing  
    Label Label   Loan ID:  
    TextBox TextBox txtSave    
    Button Button &Save btnSave  
    Label Label   Loan ID:  
    TextBox TextBox txtOpen    
    Button Button &Open btnOpen  
    Button Button btnClose Close  
  3. On the form, click the Last Name text box
  4. In the Properties window, click the Events button and double-click Leave
  5. Implement the event as follows:
     
    System::Void txtCustomerLastName_Leave(System::Object^  sender, 
    		System::EventArgs^  e)
    {
        String ^ Initials = L"00";
        String ^ FirstName = txtCustomerFirstName->Text;
        String ^ LastName = txtCustomerLastName->Text;
    
        if (LastName->Length == 0)
        {
            MessageBox::Show(L"You must enter a last name");
            txtCustomerLastName->Focus();
            return;
        }
    
        if (FirstName->Length == 0)
            Initials = LastName->Substring(0, 1)+ L"1";
        else
            Initials = FirstName->Substring(0, 1) + LastName->Substring(0, 1);
    
        txtSave->Text = Initials;
    }
  6. Return to the form
  7. On the form, double-click the Calculate button and implement its Click event as follows:
     
    System::Void btnCalculate_Click(System::Object^  sender, System::EventArgs^  e)
    {
        double Principal = 0.00;
        double InterestRate = 0.00;
        double InterestEarned = 0.00;
        double FutureValue = 0.00;
        double Periods = 0.00;
                
        // Retrieve the value of the principal
        try
        {
            Principal = double::Parse(txtPrincipal->Text);
        }
        catch(FormatException ^)
        {
            MessageBox::Show(L"The value you entered for the principal " 
                             L"is not valid.\nPlease try again");
        }
    
        // Retrieve the interest rate
        try
        {
            InterestRate = double::Parse(txtInterestRate->Text) / 100;
        }
        catch(FormatException ^)
        {
            MessageBox::Show(L"The value you entered for the interest " 
                             L"rate is not valid\nPlease try again");
        }
    
        // Get the number of periods
        try
        {
            if (cbxPeriods->SelectedIndex == 0) // Years
                Periods = double::Parse(txtPeriods->Text);
            else if (cbxPeriods->SelectedIndex == 1) // Months
                Periods = double::Parse(txtPeriods->Text) / 12;
            else // if (cbxPeriods->SelectedIndex == 2)  Days
                Periods = double::Parse(txtPeriods->Text) / 360;
        }
        catch(FormatException ^)
        {
            MessageBox::Show(L"The value you entered for the number " 
                             L"of periods is not valid\nPlease try again");
        }
    
        double InterestRatePeriods = InterestRate * Periods;
        double InterestPlus1 = InterestRatePeriods + 1;
        FutureValue = Principal * InterestPlus1;
        InterestEarned = FutureValue - Principal;
    
        txtInterestEarned->Text = InterestEarned.ToString(L"F");
        txtFutureValue->Text = FutureValue.ToString(L"F");
    }
  8. Execute the application to make sure it is fine
  9. After using it, close the form and return to your programming environment

Serialization With SOAP

To serialize an object using SOAP, you follow the same steps we reviewed for the binary serialization with one addition: you must add a certain reference.

When creating the class whose objects would be serialized, mark it with the [Serializable] attribute. Here is an example:

#pragma once
using namespace System;

[Serializable]
ref class CCar
{
public:
    CCar(void);

    String ^ Make;
    String ^ Model;
    int Year;
    int Color;
};

To support SOAP serialization, the .NET Framework provides the SoapFormatter class. This class is defined in the System::Runtime::Serialization::Formatters::Soap namespace that is part of the System.Runtime.Serialization.Formatters.Soap.dll assembly. In order to use The SoapFormatter class, you must reference this assembly. Then, you can create an object and initialize it as you see fit. Before saving it, as always, create a Stream-based object that would indicate the name (and location) of the file and the type of action to perform. Then, declare a SoapFormatter variable using its default constructor. To actually save the object, call the Serialize() method of this class. This method uses the same syntax as that of the BinaryFormatter class: it takes two arguments. The first is a Stream-based object. The second is the object that needs to be serialized.

Practical Learning: Serializing With SOAP

  1. To create a new class, on the main menu, click Project -> Add Class...
  2. Select C++ Class and click Add
  3. Set the Name to CLoanInformation and click Add
  4. Change the file as follows:
     
    #pragma once
    using namespace System;
    
    [Serializable]
    public ref class CLoanInformation
    {
    public:
    	CLoanInformation(void);
    
    	String ^ EmployeeName;
    	String ^ CustomerFirstName;
    	String ^ CustomerLastName;
    	double Principal;
    	double InterestRate;
    	double Periods;
    	int PeriodType;
    };
  5. To add SOAP support to your project, on the main menu, click Project -> WattsALoan3 Properties...
  6. Click Add New Reference...
  7. In the Add Reference dialog box and in the .NET tab, scroll down and select System::Runtime::Serialization::Formatters::Soap:
     
    Adding a reference to the System.Runtime.Serialization.Formatters.Soap.dll assembly
  8. Click OK
  9. Click OK
  10. On the form, double-click the Save button
  11. In the top section of the form, type the following:
     
    #pragma once
    #include "LoanInformation.h"
    
    namespace WattsALoan3 {
    
    	using namespace System;
    	using namespace System::ComponentModel;
    	using namespace System::Collections;
    	using namespace System::Windows::Forms;
    	using namespace System::Data;
    	using namespace System::Drawing;
    using namespace System::IO;
    using namespace System::Runtime::Serialization::Formatters::Soap;
  12. Scroll down and implement the event as follows:
     
    System::Void btnSave_Click(System::Object^  sender, System::EventArgs^  e)
    {
        if (txtSave->Text->Length == 0)
        {
            MessageBox::Show(L"Please enter the customer " 
    			L"initials or a name for the loan");
            txtSave->Focus();
            return;
        }
    
        CLoanInformation ^ infLoan = gcnew CLoanInformation;
    
        infLoan->EmployeeName = txtEmployeeName->Text;
        infLoan->CustomerFirstName = txtCustomerFirstName->Text;
        infLoan->CustomerLastName = txtCustomerLastName->Text;
        infLoan->Principal = double::Parse(txtPrincipal->Text);
        infLoan->InterestRate = double::Parse(txtInterestRate->Text);
        infLoan->Periods = double::Parse(txtPeriods->Text);
        infLoan->PeriodType = cbxPeriods->SelectedIndex;
    
        FileStream ^ stmLoan = gcnew FileStream(txtSave->Text,
    					    FileMode::Create,
                                                FileAccess::Write);
        SoapFormatter ^ sfmLoan = gcnew SoapFormatter;
    
        try
        {
            sfmLoan->Serialize(stmLoan, infLoan);
    
            txtEmployeeName->Text = L"";
            txtCustomerFirstName->Text = L"";
            txtCustomerLastName->Text = L"";
            txtPrincipal->Text = L"0.00";
            txtInterestRate->Text = L"0.00";
            txtPeriods->Text = L"0";
            cbxPeriods->SelectedIndex = 0;
            txtFutureValue->Text = L"0.00";
            txtInterestEarned->Text = L"0.00";
            txtSave->Text = L"";
            txtOpen->Text = L"";
            txtEmployeeName->Focus();
        }
        finally
        {
            stmLoan->Close();
        }
    }
  13. Press Ctrl + F5 to execute the application
  14. Create, calculate, and save a few loans
     
    Watts A Loan
    Watts A Loan
  15. Close the form and return to your programming environment

De-Serialization With SOAP

De-serialization in soap is performed exactly as done for the binary de-serialization. To support it, the SoapFormatter class is equipped with the Deserialize() method. This method uses the same syntax as its equivalent of the BinaryFormatter class. The approach to use it is also the same.

Practical Learning: Deserializing With SOAP

  1. To deserialize, on the form, double-click the Open button and implement its event as follows: 
    System::Void btnOpen_Click(System::Object^  sender, System::EventArgs^  e)
    {
        if (txtOpen->Text->Length == 0)
        {
            MessageBox::Show(L"Please enter a customer's initials or " 
                             L"a name given to a previous loan preparation");
            txtOpen->Focus();
            return;
        }
    
        try
        {
            CLoanInformation ^ infLoan = gcnew CLoanInformation;
            FileStream ^ stmLoan = gcnew FileStream(txtOpen->Text,
                                         FileMode::Open,
                                         FileAccess::ReadWrite);
            SoapFormatter ^ sfmLoan = gcnew SoapFormatter;
    
            try
            {
                // Open the file and store its values 
                // in the LoanInformation variable
                infLoan = dynamic_cast<CLoanInformation ^>(sfmLoan->Deserialize(stmLoan));
    
                // Retrive each value and put it in its corresponding control
                txtEmployeeName->Text = infLoan->EmployeeName;
                txtCustomerFirstName->Text = infLoan->CustomerFirstName;
                txtCustomerLastName->Text = infLoan->CustomerLastName;
                txtPrincipal->Text = infLoan->Principal.ToString(L"F");
                txtInterestRate->Text = infLoan->InterestRate.ToString(L"F");
                txtPeriods->Text = infLoan->Periods.ToString();
                cbxPeriods->SelectedIndex = infLoan->PeriodType;
    
                // Since we didn't save the calculated values,
                // call the Click event of the Calculate button
                btnCalculate_Click(sender, e);
            }
            finally
            {
                stmLoan->Close();
            }
        }
        catch (FileNotFoundException ^)
        {
            MessageBox::Show(L"There is no file with that name");
        }
    }
  2. Return to the form and double-click the Close button
  3. Implement its Click event as follows:
     
    System::Void btnClose_Click(System::Object^  sender, System::EventArgs^  e)
    {
        Close();
    }
  4. Press Ctrl + F5 to execute the application
  5. Enter the initials of a previously created loan and click Open
     
    Watts A Loan
  6. Close the form and return to your programming environment

Details on Serialization

 

Partial Serialization

In the examples we have used so far, we were saving the whole object. You can make it possible to save only some parts of the class. When creating a class, you can specify what fields would be serialized and which ones would not be. To specify that a member cannot be saved, you can mark it with the [NonSerialized] attribute. Here is an example:

#pragma once
using namespace System;

[Serializable]
ref class CCar
{
public:
    String ^ Make;
    String ^ Model;
    CCar(void);


    // Because the value of a car can change,
    // there is no reason to save it
    [NonSerialized]
    double Value
    int Year;
    int Color;
};

After creating the class, you can declare a variable of it and serialize it, using either the binary or the SOAP approach. You can then retrieve the object and its values, using any of the techniques we learned earlier.

Implementing a Custom Serialized Class

To support serialization, the .NET Framework provides the ISerializable interface. You can create a class that implements this interface to customize the serialization process. Even if you plan to use this interface, the class you create must be marked with the [Serializable] attribute.

.NET Built-In Serialized Classes

The .NET Framework is filled with many classes ready for serialization. To know that a class is ready for serialization, when viewing its documentation either in the MSDN web site or in the help documentation, check that it is marked with the [SerializableAttribute]. Here is an example of such as class:

The Serializable attribute of a built-in class

Some of these classes provide the properties and methods to create an object and directly save it. For some other classes, you must first create a class, mark it with the [Serializable] attribute, build an object of it, and then pass it to the .NET class.

Files
Real Estate 1
Watts A Loan 3
 
 
 
   
 

Home Copyright © 2009-2010 FunctionX, Inc.