Serialization
Serialization
Object Serialization and De-Serialization |
Introduction |
Consider the following program:
using namespace System; using namespace System::IO; int main() { String ^ Make = L"Ford"; String ^ Model = L"Escort"; int Year = 1998; int Color = 1; FileStream ^ stmCar = gcnew FileStream(L"FordEscort.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(); } return 0; }
This is an example of the techniques we have used in previous lessons to save individual data of primitive types:
The values can be retrieved with the following code:
using namespace System; using namespace System::IO; int main() { String ^Make, ^Model; int Year, Color; FileStream ^ stmCar = gcnew FileStream(L"FordEscort.car", FileMode::Open); BinaryReader ^ bnrCar = gcnew BinaryReader(stmCar); try { Console::WriteLine(L"Make: {0}", bnrCar->ReadString()); Console::WriteLine(L"Model: {0}", bnrCar->ReadString()); Console::WriteLine(L"Year: {0}", bnrCar->ReadInt32()); Console::Write(L"Color: "); int clr = bnrCar->ReadInt32(); switch(clr) { case 1: Console::WriteLine(L"Black"); break; case 2: Console::WriteLine(L"Gray"); break; case 3: Console::WriteLine(L"White"); break; case 4: Console::WriteLine(L"Red"); break; case 5: Console::WriteLine(L"Blue"); break; } } finally { bnrCar->Close(); stmCar->Close(); } return 0; }
This would produce:
Make: Ford Model: Escort Year: 1998 Color: Black Press any key to continue . . .
In the same way, we learned to save the individual fields of a class:
Here is an example:
using namespace System; using namespace System::IO; public ref class CCar { public: String ^ Make; String ^ Model; int Year; int Color; CCar(); }; CCar::CCar() { Make = L"Toyota"; Model = L"Corolla"; Year = 2002; Color = 2; } int main() { CCar ^ vehicle = gcnew CCar; FileStream ^ stmCar = File::Create(L"ToyotaCorolla.car"); BinaryWriter ^ bnwCar = gcnew BinaryWriter(stmCar); try { bnwCar->Write(vehicle->Model); bnwCar->Write(vehicle->Year); bnwCar->Write(vehicle->Color); } finally { bnwCar->Close(); stmCar->Close(); } 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 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.
using namespace System; using namespace System::IO; void HireEmployee() { FileStream ^ fsEmployee = nullptr; BinaryWriter ^ bwEmployee = nullptr; // Ask the clerk to enter an employee number // using the format 00-000 Console::Write(L"Enter Employee Number (00-000): "); String ^emplNumber = Console::ReadLine(); String ^ strPath = L"C:\\Georgetown Cleaning Services\\Employees\\" + emplNumber + L".gce"; if( File::Exists(strPath) ) { Console::Write(L"\nEither the employee has already been hired, "); Console::WriteLine(L"or there is already another employee with that number."); return; } else // If no employee with that number was found, create it { try { // If there is not yet a directory named Employees, then create it Directory::CreateDirectory(L"C:\\Georgetown Cleaning Services\\Employees"); } catch(DirectoryNotFoundException ^) { Console::WriteLine(L"The folder could not be created"); } try { fsEmployee = File::Create(strPath); bwEmployee = gcnew BinaryWriter(fsEmployee); Console::Write(L"Enter Employee First Name: "); String ^emplFName = Console::ReadLine(); Console::Write(L"Enter Employee Last Name: "); String ^emplLName = Console::ReadLine(); Console::Write(L"Enter Hourly Salary: "); double emplSalary = double::Parse(Console::ReadLine()); // The minimum salary in this company is 7.50 if( emplSalary < 7.50 ) emplSalary = 7.50; bwEmployee->Write(emplNumber); bwEmployee->Write(emplFName); bwEmployee->Write(emplLName); bwEmployee->Write(emplSalary); } finally { bwEmployee->Close(); fsEmployee->Close(); } } Console::WriteLine(); } int main() { wchar_t answer = L'0'; do { try { Console::WriteLine(L"What do you want to do?"); Console::WriteLine(L"0. Quit"); Console::WriteLine(L"1. Hire a new employee"); Console::WriteLine(L"2. Process a payroll"); Console::Write(L"Your Choice: "); answer = wchar_t::Parse(Console::ReadLine()); } catch(FormatException ^) { Console::WriteLine(L"Invalid Answer!"); } switch(answer) { case L'1': HireEmployee(); break; case L'2': break; default: break; } } while (answer == L'1' || answer == L'2'); Console::WriteLine(); return 0; }
What do you want to do? 0. Quit 1. Hire a new employee 2. Process a payroll Your Choice: 1 Enter Employee Number (00-000): 86-225 Enter Employee First Name: Anne Enter Employee Last Name: Harang Enter Hourly Salary: 16.75 What do you want to do? 0. Quit 1. Hire a new employee 2. Process a payroll Your Choice: 1 Enter Employee Number (00-000): 42-713 Enter Employee First Name: Peter Enter Employee Last Name: Lansome Enter Hourly Salary: 12.45 What do you want to do? 0. Quit 1. Hire a new employee 2. Process a payroll Your Choice: 1 Enter Employee Number (00-000): 29-368 Enter Employee First Name: Gertrude Enter Employee Last Name: Monay Enter Hourly Salary: 10.85 What do you want to do? 0. Quit 1. Hire a new employee 2. Process a payroll Your Choice: 0 Press any key to continue . . .
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:
[Serializable] public ref class CCar { public: String ^ Make; String ^ Model; int Year; int Color; CCar(); };
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. In the previous lessons, we saw how to create Stream objects (for example using the FileStream class).
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 namespace System; using namespace System::IO; using namespace System::Runtime::Serialization::Formatters::Binary; [Serializable] public ref class CCar { public: String ^ Make; String ^ Model; int Year; int Color; }; int main() { CCar ^ vehicle = gcnew CCar; vehicle->Make = L"Lexus"; vehicle->Model = L"LS"; vehicle->Year = 2007; vehicle->Color = 4; FileStream ^ stmCar = gcnew FileStream(L"LexusLS.car", FileMode::Create); BinaryFormatter ^ bfmCar = gcnew BinaryFormatter; bfmCar->Serialize(stmCar, vehicle); return 0; }
Practical Learning: Serializing an Object |
#pragma once using namespace System; [Serializable] public ref class CPayrollInformation { private: String ^ number; String ^ fname; String ^ lname; double salary; DateTime start; public: CPayrollInformation(void); property String ^ EmployeeNumber { String ^ get() { return this->number; } void set(String ^ value) { this->number = value; } } property String ^ FirstName { String ^ get() { return this->fname; } void set(String ^ value) { this->fname = value; } } property String ^ LastName { String ^ get() { return this->lname; } void set(String ^ value) { this->lname = value; } } property double HourlySalary { double get() { return this->salary; } void set(double value) { this->salary = value; } } property DateTime StartPeriod { DateTime get() { return this->start; } void set(DateTime value) { this->start = value; } } property DateTime EndPeriod { DateTime get() { return start.AddDays(13); } } double Week1Monday; double Week1Tuesday; double Week1Wednesday; double Week1Thursday; double Week1Friday; double Week2Monday; double Week2Tuesday; double Week2Wednesday; double Week2Thursday; double Week2Friday; double RegularHours; double OvertimeHours; double RegularAmount; double OvertimeAmount; double TotalEarnings; };
#include "PayrollInformation.h" using namespace System; using namespace System::IO; using namespace System::Runtime::Serialization::Formatters::Binary; void HireEmployee(); void CreatePayroll(); void SavePayroll(CPayrollInformation ^ pay); void ShowPayroll(CPayrollInformation ^ payed); void HireEmployee() { FileStream ^ fsEmployee = nullptr; BinaryWriter ^ bwEmployee = nullptr; // Ask the clerk to enter an employee number // using the format 00-000 Console::Write(L"Enter Employee Number (00-000): "); String ^emplNumber = Console::ReadLine(); String ^ strPath = L"C:\\Georgetown Cleaning Services\\Employees\\" + emplNumber + L".gce"; if( File::Exists(strPath) ) { Console::Write(L"\nEither the employee has already been hired, "); Console::WriteLine(L"or there is already another employee with that number."); return; } else // If no employee with that number was found, create it { try { // If there is not yet a directory named Employees, then create it Directory::CreateDirectory(L"C:\\Georgetown Cleaning Services\\Employees"); } catch(DirectoryNotFoundException ^) { Console::WriteLine(L"The folder could not be created"); } try { fsEmployee = File::Create(strPath); bwEmployee = gcnew BinaryWriter(fsEmployee); Console::Write(L"Enter Employee First Name: "); String ^emplFName = Console::ReadLine(); Console::Write(L"Enter Employee Last Name: "); String ^emplLName = Console::ReadLine(); Console::Write(L"Enter Hourly Salary: "); double emplSalary = double::Parse(Console::ReadLine()); // The minimum salary in this company is 7.50 if( emplSalary < 7.50 ) emplSalary = 7.50; bwEmployee->Write(emplNumber); bwEmployee->Write(emplFName); bwEmployee->Write(emplLName); bwEmployee->Write(emplSalary); } finally { bwEmployee->Close(); fsEmployee->Close(); } } Console::WriteLine(); } void CreatePayroll() { FileStream ^ fsPayroll = nullptr; BinaryReader ^ brPayroll = nullptr; DateTime dteStartDate; CPayrollInformation ^ payroll = gcnew CPayrollInformation(); double monday1 = 0.00, tuesday1 = 0.00, wednesday1 = 0.00, thursday1 = 0.00, friday1 = 0.00, monday2 = 0.00, tuesday2 = 0.00, wednesday2 = 0.00, thursday2 = 0.00, friday2 = 0.00; double totalHoursWeek1 = 0.00, totalHoursWeek2 = 0.00; double regHours1 = 0.00, regHours2 = 0.00, ovtHours1 = 0.00, ovtHours2 = 0.00; double regAmount1 = 0.00, regAmount2 = 0.00, ovtAmount1 = 0.00, ovtAmount2 = 0.00; double regularHours = 0.00, overtimeHours = 0.00; double regularAmount = 0.00, overtimeAmount = 0.00, totalEarnings = 0.00; Console::Write(L"Enter Employee Number: "); String ^emplNumber = Console::ReadLine(); String ^ strEmployeePath = L"C:\\Georgetown " L"Cleaning Services\\Employees\\" + emplNumber + ".gce"; if( !File::Exists(strEmployeePath) ) { Console::WriteLine(L"There is no employee with " L"that number in our records"); return; } // If an employee with that number was found, // continue with the payroll else { try { fsPayroll = gcnew FileStream(strEmployeePath, FileMode::Open, FileAccess::Read); brPayroll = gcnew BinaryReader(fsPayroll); payroll->EmployeeNumber = brPayroll->ReadString(); payroll->FirstName = brPayroll->ReadString(); payroll->LastName = brPayroll->ReadString(); payroll->HourlySalary = brPayroll->ReadDouble(); Console::WriteLine(L"\n------------------------" L"------------------------"); Console::WriteLine(L"Employee #: {0}", payroll->EmployeeNumber); Console::WriteLine(L"Full Name: {0}, {1}", payroll->LastName, payroll->FirstName); Console::WriteLine(L"Hourly Salary: {0:C}", payroll->HourlySalary); Console::WriteLine(L"-------------------------" L"-----------------------\n"); } finally { brPayroll->Close(); fsPayroll->Close(); } try { do { Console::Write(L"Enter Payroll Start " L"Date (mm/dd/yyyy): "); dteStartDate = DateTime::Parse(Console::ReadLine()); if( dteStartDate.DayOfWeek != DayOfWeek::Sunday ) { Console::WriteLine(L"Invalid Date Entry"); Console::WriteLine(L"Payrolls start " L"on a Sunday"); } } while (dteStartDate.DayOfWeek != DayOfWeek::Sunday); payroll->StartPeriod = dteStartDate; } catch(FormatException ^) { Console::WriteLine(L"Invalid Date Entry"); } } // Retrieve the value of each day worked Console::WriteLine(L"\nEnter the time worked " L"for each day (0.00)"); Console::WriteLine(L"=-= Week 1 =-="); try { Console::Write(L"{0}: ", payroll->StartPeriod.AddDays(1).ToString(L"D")); monday1 = double::Parse(Console::ReadLine()); } catch(FormatException ^) { Console::WriteLine(L"You typed an invalid value"); } try { Console::Write(L"{0}: ", payroll->StartPeriod.AddDays(2).ToString(L"D")); tuesday1 = double::Parse(Console::ReadLine()); } catch(FormatException ^) { Console::WriteLine(L"You typed an invalid value"); } try { Console::Write(L"{0}: ", payroll->StartPeriod.AddDays(3).ToString(L"D")); wednesday1 = double::Parse(Console::ReadLine()); } catch(FormatException ^) { Console::WriteLine(L"You typed an invalid value"); } try { Console::Write(L"{0}: ", payroll->StartPeriod.AddDays(4).ToString(L"D")); thursday1 = double::Parse(Console::ReadLine()); } catch(FormatException ^) { Console::WriteLine(L"You typed an invalid value"); } try { Console::Write(L"{0}: ", payroll->StartPeriod.AddDays(5).ToString(L"D")); friday1 = double::Parse(Console::ReadLine()); } catch(FormatException ^) { Console::WriteLine(L"You typed an invalid value"); } Console::WriteLine(L"=-= Week 2 =-="); try { Console::Write(L"{0}: ", payroll->StartPeriod.AddDays(8).ToString(L"D")); monday2 = double::Parse(Console::ReadLine()); } catch(FormatException ^) { Console::WriteLine(L"You typed an invalid value"); } try { Console::Write(L"{0}: ", payroll->StartPeriod.AddDays(9).ToString(L"D")); tuesday2 = double::Parse(Console::ReadLine()); } catch(FormatException ^) { Console::WriteLine(L"You typed an invalid value"); } try { Console::Write(L"{0}: ", payroll->StartPeriod.AddDays(10).ToString(L"D")); wednesday2 = double::Parse(Console::ReadLine()); } catch(FormatException ^) { Console::WriteLine(L"You typed an invalid value"); } try { Console::Write(L"{0}: ", payroll->StartPeriod.AddDays(11).ToString(L"D")); thursday2 = double::Parse(Console::ReadLine()); } catch(FormatException ^) { Console::WriteLine(L"You typed an invalid value"); } try { Console::Write(L"{0}: ", payroll->StartPeriod.AddDays(12).ToString(L"D")); friday2 = double::Parse(Console::ReadLine()); } catch(FormatException ^) { Console::WriteLine(L"You typed an invalid value"); } // Calculate the total number of hours for each week totalHoursWeek1 = monday1 + tuesday1 + wednesday1 + thursday1 + friday1; totalHoursWeek2 = monday2 + tuesday2 + wednesday2 + thursday2 + friday2; // The overtime is paid time and half double ovtSalary = payroll->HourlySalary * 1.5; // If the employee worked under 40 hours, // there is no overtime if( totalHoursWeek1 < 40 ) { regHours1 = totalHoursWeek1; regAmount1 = payroll->HourlySalary * regHours1; ovtHours1 = 0.00; ovtAmount1 = 0.00; } // If the employee worked over 40 hours, // calculate the overtime else if( totalHoursWeek1 >= 40 ) { regHours1 = 40; regAmount1 = payroll->HourlySalary * 40; ovtHours1 = totalHoursWeek1 - 40; ovtAmount1 = ovtHours1 * ovtSalary; } if( totalHoursWeek2 < 40 ) { regHours2 = totalHoursWeek2; regAmount2 = payroll->HourlySalary * regHours2; ovtHours2 = 0.00; ovtAmount2 = 0.00; } else if( totalHoursWeek2 >= 40 ) { regHours2 = 40; regAmount2 = payroll->HourlySalary * 40; ovtHours2 = totalHoursWeek2 - 40; ovtAmount2 = ovtHours2 * ovtSalary; } regularHours = regHours1 + regHours2; overtimeHours = ovtHours1 + ovtHours2; regularAmount = regAmount1 + regAmount2; overtimeAmount = ovtAmount1 + ovtAmount2; totalEarnings = regularAmount + overtimeAmount; payroll->Week1Monday = monday1; payroll->Week1Tuesday = tuesday1; payroll->Week1Wednesday = wednesday1; payroll->Week1Thursday = thursday1; payroll->Week1Friday = friday1; payroll->Week2Monday = monday2; payroll->Week2Tuesday = tuesday2; payroll->Week2Wednesday = wednesday2; payroll->Week2Thursday = thursday2; payroll->Week2Friday = friday2; payroll->RegularHours = regularHours; payroll->OvertimeHours = overtimeHours; payroll->RegularAmount = regularAmount; payroll->OvertimeAmount = overtimeAmount; payroll->TotalEarnings = totalEarnings; ShowPayroll(payroll); Console::Write(L"Do you want to save " L"the payroll (y/n): "); String ^ strAnswer = Console::ReadLine(); if( strAnswer->ToUpper() == L"Y") SavePayroll(payroll); } void SavePayroll(CPayrollInformation ^ pay) { // We will need this value to create the // name of the payroll file String ^ strMonth = L"0", ^strDay = L"0", ^strYear = L"0"; // We want the month and day to include 0 if necessary strMonth = pay->StartPeriod.Month.ToString(); if( pay->StartPeriod.Month < 10 ) strMonth = L"0" + pay->StartPeriod.Month.ToString(); strDay = pay->StartPeriod.Day.ToString(); if( pay->StartPeriod.Day < 10 ) strDay = L"0" + pay->StartPeriod.Day.ToString(); strYear = pay->StartPeriod.Year.ToString(); String ^ strPayrollFilename = L"C:\\Georgetown " L"Cleaning Services\\Payrolls\\" + pay->LastName[0] + pay->FirstName[0] + strMonth + strDay + strYear + ".epr"; try { // If there is not yet a directory for the // payrolls, then create it Directory::CreateDirectory(L"C:\\Georgetown Cleaning " L"Services\\Payrolls"); } catch(DirectoryNotFoundException ^) { Console::WriteLine(L"The employee payroll file " L"could not be created"); } if( File::Exists(strPayrollFilename) ) { Console::WriteLine(L"The employee's payroll " L"for that period exists already"); } FileStream ^ fsEmployeePayroll = gcnew FileStream(strPayrollFilename, FileMode::Create); BinaryFormatter ^ bfEmployeePayroll = gcnew BinaryFormatter(); bfEmployeePayroll->Serialize(fsEmployeePayroll, pay); fsEmployeePayroll->Close(); } void ShowPayroll(CPayrollInformation ^ payed) { Console::WriteLine(L"\n=============================" L"==================="); Console::WriteLine(L"=$= Payroll summary =$="); Console::WriteLine(L"-------------------------------" L"-----------------"); Console::WriteLine(L"Employee #: {0}", payed->EmployeeNumber); Console::WriteLine(L"Full Name: {0}, {1}", payed->FirstName, payed->LastName); Console::WriteLine(L"Hourly Salary: {0:C}", payed->HourlySalary); Console::WriteLine(L"Start Period: {0:D}", payed->StartPeriod); Console::WriteLine(L"End Period: {0:D}", payed->EndPeriod); Console::WriteLine(L"--------------------------------" L"----------------"); Console::WriteLine(L" Monday Tuesday Wednesday " L"Thursday Friday"); Console::WriteLine(L"Week 1: {0:F} {1:F} " L"{2:F} {3:F} {4:F}", payed->Week1Monday, payed->Week1Tuesday, payed->Week1Wednesday, payed->Week1Thursday, payed->Week1Friday); Console::WriteLine(L"Week 2: {0:F} {1:F} " L"{2:F} {3:F} {4:F}", payed->Week2Monday, payed->Week2Tuesday, payed->Week2Wednesday, payed->Week2Thursday, payed->Week2Friday); Console::WriteLine(L"-------------------------------" L"-----------------"); Console::WriteLine(L"Monetary Summary"); Console::WriteLine(L" Hours Amount"); Console::WriteLine(L"Regular: {0,6} {1,6}", payed->RegularHours.ToString(L"F"), payed->RegularAmount.ToString(L"F")); Console::WriteLine(L"Overtime: {0,6} {1,6}", payed->OvertimeHours.ToString(L"F"), payed->OvertimeAmount.ToString(L"F")); Console::WriteLine(L"-------------------------------" L"-----------------"); Console::WriteLine(L"Net Pay: {0:F}", payed->TotalEarnings); Console::WriteLine(L"===============================" L"=================\n"); } void ViewPayroll() { } int main() { wchar_t answer = L'0'; Console::WriteLine(L"========================" L"========================"); Console::WriteLine(L"Georgetown Cleaning Services"); Console::WriteLine(L"========================" L"========================"); do { try { Console::WriteLine(L"\nWhat do you want to do?"); Console::WriteLine(L"0. Quit"); Console::WriteLine(L"1. Hire a new employee"); Console::WriteLine(L"2. Process a payroll"); Console::WriteLine(L"3. View an employee's payroll"); Console::Write(L"Your Choice: "); answer = wchar_t::Parse(Console::ReadLine()); Console::WriteLine(); } catch(FormatException ^) { Console::WriteLine(L"Invalid Answer!"); } switch(answer) { case L'1': HireEmployee(); break; case L'2': CreatePayroll(); break; case L'3': ViewPayroll(); break; default: break; } } while( (answer == L'1') || (answer == L'2') || (answer == L'3') ); Console::WriteLine(); return 0; } ================================================ Georgetown Cleaning Services ================================================ What do you want to do? 0. Quit 1. Hire a new employee 2. Process a payroll 3. View an employee's payroll Your Choice: 2 Enter Employee Number: 29-368 ------------------------------------------------ Employee #: 29-368 Full Name: Monay, Gertrude Hourly Salary: $10.85 ------------------------------------------------ Enter Payroll Start Date (mm/dd/yyyy): 11/12/05 Invalid Date Entry Payrolls start on a Sunday Enter Payroll Start Date (mm/dd/yyyy): 11/12/06 Enter the time worked for each day (0.00) =-= Week 1 =-= Monday, November 13, 2006: 8.00 Tuesday, November 14, 2006: 8.50 Wednesday, November 15, 2006: 9.50 Thursday, November 16, 2006: 8 Friday, November 17, 2006: 8.50 =-= Week 2 =-= Monday, November 20, 2006: 6.50 Tuesday, November 21, 2006: 7.00 Wednesday, November 22, 2006: 8 Thursday, November 23, 2006: 6.00 Friday, November 24, 2006: 7.00 ================================================ =$= Payroll summary =$= ------------------------------------------------ Employee #: 29-368 Full Name: Monay, Gertrude Hourly Salary: $10.85 Start Period: Sunday, November 12, 2006 End Period: Saturday, November 25, 2006 ------------------------------------------------ Monday Tuesday Wednesday Thursday Friday Week 1: 8.00 8.50 9.50 8.00 8.50 Week 2: 6.50 7.00 8.00 6.00 7.00 ------------------------------------------------ Monetary Summary Hours Amount Regular: 74.50 808.33 Overtime: 2.50 40.69 ------------------------------------------------ Net Pay: 849.01 ================================================ Do you want to save the payroll (y/n): Y What do you want to do? 0. Quit 1. Hire a new employee 2. Process a payroll 3. View an employee's payroll Your Choice: 0 Press any key to continue . . . ================================================ Georgetown Cleaning Services ================================================ What do you want to do? 0. Quit 1. Hire a new employee 2. Process a payroll 3. View an employee's payroll Your Choice: 2 Enter Employee Number: 86-025 There is no employee with that number in our records What do you want to do? 0. Quit 1. Hire a new employee 2. Process a payroll 3. View an employee's payroll Your Choice: 2 Enter Employee Number: 86-225 ------------------------------------------------ Employee #: 86-225 Full Name: Harang, Anne Hourly Salary: $16.75 ------------------------------------------------ Enter Payroll Start Date (mm/dd/yyyy): 11/26/2006 Enter the time worked for each day (0.00) =-= Week 1 =-= Monday, November 27, 2006: 8 Tuesday, November 28, 2006: 6.5 Wednesday, November 29, 2006: 8.5 Thursday, November 30, 2006: 8 Friday, December 01, 2006: 8 =-= Week 2 =-= Monday, December 04, 2006: 9 Tuesday, December 05, 2006: 8.5 Wednesday, December 06, 2006: 8 Thursday, December 07, 2006: 9.5 Friday, December 08, 2006: 8.00 ================================================ =$= Payroll summary =$= ------------------------------------------------ Employee #: 86-225 Full Name: Anne, Harang Hourly Salary: $16.75 Start Period: Sunday, November 26, 2006 End Period: Saturday, December 09, 2006 ------------------------------------------------ Monday Tuesday Wednesday Thursday Friday Week 1: 8.00 6.50 8.50 8.00 8.00 Week 2: 9.00 8.50 8.00 9.50 8.00 ------------------------------------------------ Monetary Summary Hours Amount Regular: 79.00 1323.25 Overtime: 3.00 75.38 ------------------------------------------------ Net Pay: 1398.63 ================================================ Do you want to save the payroll (y/n):
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. 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 namespace System; using namespace System::IO; using namespace System::Runtime::Serialization::Formatters::Binary; [Serializable] public ref class CCar { public: String ^Make; String ^Model; int Year; int Color; }; int main() { FileStream ^ stmCar = gcnew FileStream(L"LexusLS.car", FileMode::Open); BinaryFormatter ^ bfmCar = gcnew BinaryFormatter(); CCar ^ vehicle = dynamic_cast<CCar ^>(bfmCar->Deserialize(stmCar)); Console::WriteLine(L"Make: {0}", vehicle->Make); Console::WriteLine(L"Model: {0}", vehicle->Model); Console::WriteLine(L"Year: {0}", vehicle->Year); Console::Write(L"Color: "); int clr = vehicle->Color; switch(clr) { case 1: Console::WriteLine(L"Black"); break; case 2: Console::WriteLine(L"Gray"); break; case 3: Console::WriteLine(L"White"); break; case 4: Console::WriteLine(L"Red"); break; case 5: Console::WriteLine(L"Blue"); break; } stmCar->Close(); return 0; } This would produce: Make: Lexus Model: LS Year: 2007 Color: Red Press any key to continue . . .
The .NET Framework supports another technique of serialization referred to as SOAP (which stands for Simple Object Access Protocol). This technique a little more related to XML but, although we haven't studied XML, you don't need to know anything about it to use SOAP serialization. To serialize an object using SOAP, you follow the same steps we reviewed for the binary serialization with one addition that 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: [Serializable] public ref class CCar { public: 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. You can do this by adding it to your list of references: After these two steps, 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. Here is an example:
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. Here is an example: using namespace System; using namespace System::IO; using namespace System::Runtime::Serialization::Formatters::Soap; [Serializable] public ref class CCar { public: String ^ Make; String ^ Model; int Year; int Color; }; int main() { FileStream ^ stmCar = gcnew FileStream(L"VolvoS40.car", FileMode::Open); SoapFormatter ^ sopCar = gcnew SoapFormatter(); CCar ^ vehicle = dynamic_cast<CCar ^>(sopCar->Deserialize(stmCar)); Console::WriteLine(L"Car Information"); Console::WriteLine(L"Make: {0}", vehicle->Make ); Console::WriteLine(L"Model: {0}", vehicle->Model); Console::WriteLine(L"Year: {0}", vehicle->Year); Console::Write(L"Color: "); switch(vehicle->Color) { case 1: Console::WriteLine(L"Black"); break; case 2: Console::WriteLine(L"Gray"); break; case 3: Console::WriteLine(L"White"); break; case 4: Console::WriteLine(L"Red"); break; case 5: Console::WriteLine(L"Blue"); break; } return 0; } This would produce: Car Information Make: Volvo Model: S40 Year: 2006 Color: White Press any key to continue . . .
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: [Serializable] public ref class CCar { public: String ^Make; String ^Model; // Because the value of a car can change, // there is no reason to save it [NonSerialized] public: 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. Here is an example: using namespace System; using namespace System::IO; using namespace System::Runtime::Serialization::Formatters::Binary; [Serializable] public ref class CCar { public: String ^Make; String ^Model; // Because the value of a car can change, // there is no reason to save it [NonSerialized] public: double Value; int Year; int Color; } int main() { CCar vehicle = gcnew CCar; vehicle->Make = L"Lexus"; vehicle->Model = L"LS"; vehicle->Year = 2007; vehicle->Color = 4; vehicle->Value = 28640M; FileStream ^ stmCar = gcnew FileStream(L"LexusLS.car", FileMode::Create); BinaryFormatter ^ bfmCar = gcnew BinaryFormatter; bfmCar->Serialize(stmCar, vehicle); return 0; } } You can then retrieve the object and its values, using any of the techniques we learned earlier. Here is an example: using namespace System; using namespace System::IO; using namespace System::Runtime::Serialization::Formatters::Binary; [Serializable] public ref class CCar { public: String ^Make; String ^Model; // Because the value of a car can change, // there is no reason to save it [NonSerialized] public: double Value; int Year; int Color; } int main() { FileStream ^ stmCar = gcnew FileStream(L"LexusLS.car", FileMode::Open); BinaryFormatter bfmCar = new BinaryFormatter(); CCar ^ vehicle = dynamic_cast<CCar ^>(bfmCar->Deserialize(stmCar)); Console::WriteLine(L"Car Information"); Console::WriteLine(L"Make: {0}", vehicle->Make); Console::WriteLine(L"Model: {0}", vehicle->Model); Console::WriteLine(L"Year: {0}", vehicle->Year); Console::Write(L"Color: "); switch (vehicle->Color) { case 1: Console::WriteLine(L"Black"); break; case 2: Console::WriteLine(L"Gray"); break; case 3: Console::WriteLine(L"White"); break; case 4: Console::WriteLine(L"Red"); break; case 5: Console::WriteLine(L"Blue"); break; } Console::WriteLine(L"Value: {0}\n", vehicle->Value); return 0; } This would produce: Car Information Make: Lexus Model: LS Year: 2007 Color: Red Value: 0 Press any key to continue . . . Notice that the value of the Value field was not saved: it holds the default value of its data type. This indicates that, you can assign a value a [NonSerialized] field and save the object. The value of that field would not be saved but the compiler would not throw an exception.
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.
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: 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.
|