Object Serialization and De-Serialization |
|
|
Consider the following form:
|
#pragma once
namespace CarReview
{
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;
---------------------------------------------------
System::Void btnWrite_Click(System::Object^ sender, System::EventArgs^ e)
{
String ^ Make = txtMake->Text;
String ^ Model = txtModel->Text;
int Year = int::Parse(txtYear->Text);
String ^ Color = txtColor->Text;
FileStream ^ stmCar = gcnew FileStream("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();
}
}
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:
The values can be retrieved with the following code:
System::Void btnRead_Click(System::Object^ sender, System::EventArgs^ e)
{
FileStream ^ stmCar = gcnew FileStream("Car1.car", FileMode::Open);
BinaryReader ^ bnrCar = gcnew BinaryReader(stmCar);
try
{
txtMake->Text = bnrCar->ReadString();
txtModel->Text = bnrCar->ReadString();
txtYear->Text = bnrCar->ReadUInt32().ToString();
txtColor->Text = bnrCar->ReadString();
}
finally
{
bnrCar->Close();
stmCar->Close();
}
}
In the same way, you can save the individual fields of
a class or you can retrieve the individual fields of a car:
Here is an example:
ref class CCar
{
public:
String ^ Make;
String ^ Model;
int Year;
String ^ Color;
};
private:
System::Void btnWrite_Click(System::Object^ sender, System::EventArgs^ e)
{
CCar ^ vehicle = gcnew CCar;
vehicle->Make = txtMake->Text;
vehicle->Model = txtModel->Text;
vehicle->Year = int::Parse(txtYear->Text);
vehicle->Color = txtColor->Text;
FileStream ^ stmCar = gcnew FileStream("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();
}
}
System::Void btnRead_Click(System::Object^ sender, System::EventArgs^ e)
{
FileStream ^ stmCar = gcnew FileStream("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->ReadString();
txtMake->Text = vehicle->Make;
txtModel->Text = vehicle->Model;
txtYear->Text = vehicle->Year.ToString();
txtColor->Text = vehicle->Color;
}
finally
{
bnrCar->Close();
stmCar->Close();
}
}
};
}
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
CCar object.
Object serialization consists of saving a whole object
as one instead of its individual fields:
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
|
|
- Start Microsoft Visual Studio
- To start a new project, on the main menu, click File -> New
Project...
- In the middle list, click Windows Forms Application
- Set the Name to SchoolCatalog1
- Click OK
- To create a new form, on the main menu, click Project -> Add New
Item...
- In the middle list, make sure Windows Form is selected.
Set the
Name to CourseEditor
- Click Add
- Design the form as follows:
|
Control |
(Name) |
DialogResult |
Text |
Modifiers |
Label |
|
|
|
Course Code: |
|
TextBox |
|
txtCourseCode |
|
|
Public |
Label |
|
|
|
Credits: |
|
TextBox |
|
txtCredits |
|
1 |
Public |
Label |
|
|
|
Course Name: |
|
TextBox |
|
txtCourseName |
|
|
Public |
Button |
|
btnOK |
OK |
OK |
|
Button |
|
btnCancel |
Cancel |
Cancel |
|
|
Form |
FormBorderStyle: |
FixedDialog |
Text: |
University Catalog - Course Editor |
StartPosition: |
CenterScreen |
AcceptButton: |
btnOK |
CancelButton: |
btnCancel |
MaximizeBox: |
False |
MinimizeBox: |
False |
ShowInTaskBar: |
False |
- Display the first form and design it as follows:
|
Control |
(Name) |
Anchor |
Text |
DataGridView |
|
dgvCourseCataglog |
Top, Bottom, Left, Right |
|
Columns |
|
Header Text |
Name |
Width |
Course Code |
CourseCode |
|
Course Name |
CourseName |
200 |
Credits |
Credits |
60 |
|
Button |
|
btnNewCourse |
Bottom, Left |
&New Course |
Button |
|
btnClose |
Bottom, Right |
&Close |
|
Form |
Text: |
University Catalog - Course Catalog |
StartPosition: |
CenterScreen |
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:
[Serializable]
public ref class CCar
{
public:
String ^ Make;
String ^ Model;
int Year;
String ^ 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() member function of the
BinaryFormatter class. The member function is overloaded with two
versions. One of the versions of this member function uses the following
syntax:
public:
virtual void Serialize(Stream^ serializationStream,
Object^ graph) sealed;
The first argument to this member function 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 member function, you should have built the object.
Here is an example:
#pragma once
namespace CarReview {
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;
[Serializable]
ref class CCar
{
public:
String ^ Make;
String ^ Model;
int Year;
String ^ Color;
};
--------------------------------------------------------------------
System::Void btnWrite_Click(System::Object^ sender, System::EventArgs^ e)
{
CCar ^ vehicle = gcnew CCar;
vehicle->Make = txtMake->Text;
vehicle->Model = txtModel->Text;
vehicle->Year = int::Parse(txtYear->Text);
vehicle->Color = txtColor->Text;
FileStream ^ stmCar = gcnew FileStream("Car3.car",
FileMode::Create);
BinaryFormatter ^ bfmCar = gcnew BinaryFormatter;
bfmCar->Serialize(stmCar, vehicle);
}
Practical
Learning: Serializing an Object
|
|
- To create a new class, on the main menu, click Project -> Add
Class...
- In the middle list, click C++ Class
- Click Add
- Set the Name to CCourse
- Click Finish
- Change the header file as follows:
#pragma once
using namespace System;
[Serializable]
public ref class CCourse
{
public:
CCourse(void);
String ^ CourseCode;
String ^ CourseName;
int Credits;
};
- On the main menu, click Windows -> Form1.h [Design]*
- Right-click the form and click View Code
- In the top section of the file, add a reference to the
System::IO, the Collections::Generic, and
the Formatters::Binary namespaces
- Also, declare a List<> variable named
courses:
#pragma once
#include "Course.h"
#include "CourseEditor.h"
namespace SchoolCatalog1 {
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::Collections::Generic;
using namespace System::Runtime::Serialization::Formatters::Binary;
/// <summary>
/// Summary for Form1
/// </summary>
public ref class Form1 : public System::Windows::Forms::Form
{
private:
List<CCourse ^> ^ courses;
- Return to the main menu and double-click an unoccupied area of its
body
- Initialize the courses variable as follows:
System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e)
{
courses = gcnew List<CCourse ^>;
}
- Return to the form
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() member function. Like
Serialize(), the Deserialize() member function is overloaded
with two versions. One of them uses the following syntax:
public:
virtual Object^ Deserialize(Stream^ serializationStream) sealed;
This member function takes as argument a Stream-based
object, such as a FileStream variable, that indicates where the
file is located. The Deserialize() member function returns an
Object object. As a goal, you want the Deserialize() member
function to produce the type of object that was saved so you can retrieve
the values that the returned object holds. Because the member function
returns an Object value, you must cast the returned value to the
type of your class.
Once the Deserialize() member function has
returned the desired object, you can access its values. Here is an
example:
System::Void btnRead_Click(System::Object^ sender, System::EventArgs^ e)
{
FileStream ^ stmCar = gcnew FileStream("Car3.car",
FileMode::Open);
BinaryFormatter ^ bfmCar = gcnew BinaryFormatter;
CCar ^ vehicle = (CCar ^)bfmCar->Deserialize(stmCar);
txtMake->Text = vehicle->Make;
txtModel->Text = vehicle->Model;
txtYear->Text = vehicle->Year.ToString();
txtColor->Text = vehicle->Color;
}
Practical
Learning: De-Serializing an Object
|
|
- On the form, double-click New Course...
- Return to the form and double-click the Close button
- Change the file as follows:
void ShowCourses()
{
// Get a reference to the file that holds the courses
String ^ Filename = "C:\\School Catalog\\courses.sct";
// Make sure the file exists
if( File::Exists(Filename) == true)
{
// if so, create a file stream
FileStream ^ stmCourses = gcnew FileStream(Filename,
FileMode::Open,
FileAccess::Read);
// Create a binary formatter
BinaryFormatter ^ bfmCourse = gcnew BinaryFormatter;
// If some courses were created already,
// get them and store them in the collection
courses = (List<CCourse ^> ^)bfmCourse->Deserialize(stmCourses);
// Prepare to show the list of courses
int i = 0;
// First, empty the data grid view
dgvCourseCataglog->Rows->Clear();
// Visit each course in the collection and add it to the data grid view
for each(CCourse ^ study in courses)
{
dgvCourseCataglog->Rows->Add();
dgvCourseCataglog->Rows[i]->Cells[0]->Value = study->CourseCode;
dgvCourseCataglog->Rows[i]->Cells[1]->Value = study->CourseName;
dgvCourseCataglog->Rows[i]->Cells[2]->Value =
study->Credits.ToString();
i++;
}
// Close the file stream
stmCourses->Close();
}
}
System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e)
{
courses = gcnew List<CCourse ^>;
ShowCourses();
}
System::Void btnNewCourse_Click(System::Object^ sender, System::EventArgs^ e)
{
// Get a reference to the course editor
CourseEditor ^ editor = gcnew CourseEditor;
// Check that the directory that contains the list of courses exists.
// If it doesn't exist, create it
DirectoryInfo ^ dirInfo = Directory::CreateDirectory("C:\\School Catalog");
// Get a reference to the file that holds the courses
String ^ Filename = "C:\\School Catalog\\courses.sct";
// First check if the file was previously created
if( File::Exists(Filename) == true)
{
// If the list of courses exists already,
// get it and store it in a file stream
FileStream ^ stmCourses = gcnew FileStream(Filename,
FileMode::Open,
FileAccess::Read);
BinaryFormatter ^ bfmCourse = gcnew BinaryFormatter;
// Store the list of courses in the collection
courses = (List<CCourse ^> ^)bfmCourse->Deserialize(stmCourses);
// Close the file stream
stmCourses->Close();
}
// Display the Course Editor
if( editor->ShowDialog() == System::Windows::Forms::DialogResult::OK )
{
// Get each value of the new course.
// Create a Course object from it
CCourse ^ study = gcnew CCourse;
study->CourseCode = editor->txtCourseCode->Text;
study->CourseName = editor->txtCourseName->Text;
study->Credits = int::Parse(editor->txtCredits->Text);
// Add the course in the collection
courses->Add(study);
// Get a reference to the courses file
String ^ strFilename = dirInfo->FullName + "\\\courses.sct";
// Create a file stream to hold the list of courses
FileStream ^ stmCourses = gcnew FileStream(strFilename,
FileMode::Create,
FileAccess::Write);
BinaryFormatter ^ bfmCourses = gcnew BinaryFormatter;
// Serialize the list of courses
bfmCourses->Serialize(stmCourses, courses);
// Close the file stream
stmCourses->Close();
}
// Show the list of courses
ShowCourses();
}
System::Void btnClose_Click(System::Object^ sender, System::EventArgs^ e)
{
Close();
}
- Execute the application and continuously click the New Course
button to create the following courses:
Course Code |
Course Name |
Credits |
CMSC 101 |
Introductory Computer Science |
3 |
ENGL 101 |
Introduction to English |
3 |
BMGT 324 |
Introduction to Entrepreneurship: Starting a Small
Business |
1 |
BIOL 160 |
Human Biology |
3 |
BMGT 110 |
Introduction to Business and Management |
3 |
CMSC 150 |
Introduction to Discrete Structures |
3 |
CMSC 330 |
Advanced Programming Languages |
3 |
CHEM 121 |
Chemistry in the Modern World |
3 |
- Close the form and return to your programming environment