Object Serialization and De-Serialization |
|
|
Consider the following form:
|
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace Cars1
{
public partial class Exercise : Form
{
public Exercise()
{
InitializeComponent();
}
private void btnWrite_Click(object sender, EventArgs e)
{
var Make = txtMake.Text;
var Model = txtModel.Text;
var Year = uint.Parse(txtYear.Text);
var Color = txtColor.Text;
var stmCar = new FileStream("Car1.car", FileMode.Create);
var bnwCar = new 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:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace Cars1
{
public partial class Exercise : Form
{
public Exercise()
{
InitializeComponent();
}
private void btnWrite_Click(object sender, EventArgs e)
{
var Make = txtMake.Text;
var Model = txtModel.Text;
var Year = uint.Parse(txtYear.Text);
var Color = txtColor.Text;
var stmCar = new FileStream("Car1.car", FileMode.Create);
var bnwCar = new BinaryWriter(stmCar);
try
{
bnwCar.Write(Make);
bnwCar.Write(Model);
bnwCar.Write(Year);
bnwCar.Write(Color);
}
finally
{
bnwCar.Close();
stmCar.Close();
}
}
private void btnRead_Click(object sender, EventArgs e)
{
var stmCar = new FileStream("Car1.car", FileMode.Open);
var bnrCar = new 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:
Class: Car.cs |
using System;
namespace Cars1
{
public class Car
{
public string Make;
public string Model;
public uint Year;
public int Color;
}
}
|
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace Cars1
{
public partial class Exercise : Form
{
public Exercise()
{
InitializeComponent();
}
private void btnWrite_Click(object sender, EventArgs e)
{
var vehicle = new Car();
vehicle.Make = txtMake.Text;
vehicle.Model = txtModel.Text;
vehicle.Year = uint.Parse(txtYear.Text);
vehicle.Color = txtColor.Text;
var stmCar = new FileStream("Car2.car", FileMode.Create);
var bnwCar = new BinaryWriter(stmCar);
try
{
bnwCar.Write(vehicle.Make);
bnwCar.Write(vehicle.Model);
bnwCar.Write(vehicle.Year);
bnwCar.Write(vehicle.Color);
}
finally
{
bnwCar.Close();
stmCar.Close();
}
}
private void btnRead_Click(object sender, EventArgs e)
{
var stmCar = new FileStream("Car2.car", FileMode.Open);
var bnrCar = new BinaryReader(stmCar);
try
{
var vehicle = new Car();
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 Car class, if you don't save the Make information
of a Car 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:
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
- Create a Windows Forms Application named SchoolCatalog1
- To create a new form, on the main menu, click Projects -> Add
Windows Form...
- Set the Name to CourseEditor and 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 |
- In the Solution Explorer, right-click Form1.cs and click Rename
- Type SchoolCatalog.cs and press Enter twice (to display
that form)
- Design the form 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:
using System;
namespace Cars1
{
[Serializable]
public class Car
{
public string Make;
public string Model;
public uint Year;
public 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 void Serialize(Stream serializationStream, object graph);
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:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Cars1
{
public partial class Exercise : Form
{
public Exercise()
{
InitializeComponent();
}
private void btnWrite_Click(object sender, EventArgs e)
{
Car vehicle = new Car();
vehicle.Make = txtMake.Text;
vehicle.Model = txtModel.Text;
vehicle.Year = uint.Parse(txtYear.Text);
vehicle.Color = txtColor.Text;
FileStream stmCar = new FileStream("Car3.car",
FileMode.Create);
BinaryFormatter bfmCar = new BinaryFormatter();
bfmCar.Serialize(stmCar, vehicle);
}
}
}
Practical
Learning: Serializing an Object
|
|
- To create a new class, on the main menu, click PROJECT -> Add
Class...
- Set the Name to Course and click Add
- Change the file as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SchoolCatalog1
{
[Serializable]
public class Course
{
public string CourseCode;
public string CourseName;
public int Credits;
}
}
- On the main menu, click Windows -> SchoolCatalog.cs [Design]*
- Double-click an unoccupied area of the form
- Change the file as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace SchoolCatalog1
{
public partial class SchoolCatalog : Form
{
List<Course> courses;
public SchoolCatalog()
{
InitializeComponent();
}
private void SchoolCatalog_Load(object sender, EventArgs e)
{
courses = new List<Course>();
}
}
}
- 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() method. Like Serialize(),
the Deserialize() method is overloaded with two versions. One of
them uses the following syntax:
public object Deserialize(Stream serializationStream);
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:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Cars1
{
public partial class Exercise : Form
{
public Exercise()
{
InitializeComponent();
}
private void btnWrite_Click(object sender, EventArgs e)
{
Car vehicle = new Car();
vehicle.Make = txtMake.Text;
vehicle.Model = txtModel.Text;
vehicle.Year = uint.Parse(txtYear.Text);
vehicle.Color = txtColor.Text;
FileStream stmCar = new FileStream("Car3.car",
FileMode.Create);
BinaryFormatter bfmCar = new BinaryFormatter();
bfmCar.Serialize(stmCar, vehicle);
}
private void btnRead_Click(object sender, EventArgs e)
{
FileStream stmCar = new FileStream("Car3.car",
FileMode.Open);
BinaryFormatter bfmCar = new BinaryFormatter();
Car vehicle = (Car)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:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace SchoolCatalog1
{
public partial class SchoolCatalog : Form
{
List<Course> courses;
public SchoolCatalog()
{
InitializeComponent();
}
private void SchoolCatalog_Load(object sender, EventArgs e)
{
courses = new List<Course>();
ShowCourses();
}
private 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 = new FileStream(Filename,
FileMode.Open,
FileAccess.Read);
// Create a binary formatter
BinaryFormatter bfmCourse = new BinaryFormatter();
// If some courses were created already,
// get them and store them in the collection
courses = (List<Course>)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
foreach (Course 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();
}
}
private void btnNewCourse_Click(object sender, EventArgs e)
{
// Get a reference to the course editor
CourseEditor editor = new 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 = new FileStream(Filename,
FileMode.Open,
FileAccess.Read);
BinaryFormatter bfmCourse = new BinaryFormatter();
// Store the list of courses in the collection
courses = (List<Course>)bfmCourse.Deserialize(stmCourses);
// Close the file stream
stmCourses.Close();
}
// Display the Course Editor
if (editor.ShowDialog() == DialogResult.OK)
{
// Get each value of the new course.
// Create a Course object from it
Course study = new Course();
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 = new FileStream(strFilename,
FileMode.Create,
FileAccess.Write);
BinaryFormatter bfmCourses = new BinaryFormatter();
// Serialize the list of courses
bfmCourses.Serialize(stmCourses, courses);
// Close the file stream
stmCourses.Close();
}
// Show the list of courses
ShowCourses();
}
private void btnClose_Click(object sender, 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