Creating a Sequential-Access File Using Object Serialization
We begin by creating and writing serialized objects to a sequential-access file. In this section, we reuse much of the code from Section 18.5, so we focus only on the new features.
Defining the RecordSerializable Class
Let us begin by modifying our Record class (Fig. 18.8) so that objects of this class can be serialized. Class RecordSerializable (Fig. 18.13) is marked with the [Serializable] attribute (line 5), which indicates to the CLR that objects of class Record can be serialized. The classes for objects that we wish to write to or read from a stream must include this attribute in their declarations or must implement interface ISerializable. Class RecordSerializable contains private data members account, firstName, lastName and balance. This class also provides public properties for accessing the private fields.
Figure 18.13. RecordSerializable class for serializable objects.
1 // Fig. 18.13: RecordSerializable.cs 2 // Serializable class that represents a data record. 3 using System; 4 5 [ Serializable ] 6 public class RecordSerializable 7 { 8 private int account; 9 private string firstName; 10 private string lastName; 11 private decimal balance; 12 13 // parameterless constructor sets members to default values 14 public RecordSerializable() 15 : this( 0, "", "", 0.0M ) 16 { 17 } // end constructor 18 19 // overloaded constructor sets members to parameter values 20 public RecordSerializable( int accountValue, string firstNameValue, 21 string lastNameValue, decimal balanceValue ) 22 { 23 Account = accountValue; 24 FirstName = firstNameValue; 25 LastName = lastNameValue; 26 Balance = balanceValue; 27 } // end constructor 28 29 // property that gets and sets Account 30 public int Account 31 { 32 get 33 { 34 return account; 35 } // end get 36 set 37 { 38 account = value; 39 } // end set 40 } // end property Account 41 42 // property that gets and sets FirstName 43 public string FirstName 44 { 45 get 46 { 47 return firstName; 48 } // end get 49 set 50 { 51 firstName = value; 52 } // end set 53 } // end property FirstName 54 55 // property that gets and sets LastName 56 public string LastName 57 { 58 get 59 { 60 return lastName; 61 } // end get 62 set 63 { 64 lastName = value; 65 } // end set 66 } // end property LastName 67 68 // property that gets and sets Balance 69 public decimal Balance 70 { 71 get 72 { 73 return balance; 74 } // end get 75 set 76 { 77 balance = value; 78 } // end set 79 } // end property Balance 80 } // end class RecordSerializable |
In a class that is marked with the [Serializable] attribute or that implements interface ISerializable, you must ensure that every instance variable of the class is also serializable. All simple-type variables and strings are serializable. For variables of reference types, you must check the class declaration (and possibly its base classes) to ensure that the type is serializable. By default, array objects are serializable. However, if the array contains references to other objects, those objects may or may not be serializable.
Using a Serialization Stream to Create an Output File
Now let's create a sequential-access file with serialization (Fig. 18.14). Line 13 creates a BinaryFormatter for writing serialized objects. Lines 4849 open the FileStream to which this program writes the serialized objects. The string argument that is passed to the FileStream's constructor represents the name and path of the file to be opened. This specifies the file to which the serialized objects will be written.
Figure 18.14. Sequential file created using serialization.
1 // Fig 18.14: CreateFileForm.cs 2 // Creating a sequential-access file using serialization. 3 using System; 4 using System.Windows.Forms; 5 using System.IO; 6 using System.Runtime.Serialization.Formatters.Binary; 7 using System.Runtime.Serialization; 8 using BankLibrary; 9 10 public partial class CreateFileForm : BankUIForm 11 { 12 // object for serializing Records in binary format 13 private BinaryFormatter formatter = new BinaryFormatter(); 14 private FileStream output; // stream for writing to a file 15 16 // parameterless constructor 17 public CreateFileForm() 18 { 19 InitializeComponent(); 20 } // end constructor 21 22 // handler for saveButton_Click 23 private void saveButton_Click( object sender, EventArgs e ) 24 { 25 // create dialog box enabling user to save file 26 SaveFileDialog fileChooser = new SaveFileDialog(); 27 DialogResult result = fileChooser.ShowDialog(); 28 string fileName; // name of file to save data 29 30 fileChooser.CheckFileExists = false; // allow user to create file 31 32 // exit event handler if user clicked "Cancel" 33 if ( result == DialogResult.Cancel ) 34 return; 35 36 fileName = fileChooser.FileName; // get specified file name 37 38 // show error if user specified invalid file 39 if ( fileName == "" || fileName == null ) 40 MessageBox.Show( "Invlaid File Name", "Error", 41 MessageBoxButtons.OK, MessageBoxIcon.Error ); 42 else 43 { 44 // save file via FileStream if user specified valid file 45 try 46 { 47 // open file with write access 48 output = new FileStream( fileName, 49 FileMode.OpenOrCreate, FileAccess.Write ); 50 51 // disable Save button and enable Enter button 52 saveButton.Enabled = false; 53 enterButton.Enabled = true; 54 } // end try 55 // handle exception if there is a problem opening the file 56 catch ( IOException ) 57 { 58 // notify user if file does not exist 59 MessageBox.Show( "Error opening file", "Error", 60 MessageBoxButtons.OK, MessageBoxIcon.Error ); 61 } // end catch 62 } // end else 63 } // end method saveButton_Click 64 65 // handler for enterButton Click 66 private void enterButton_Click( object sender, EventArgs e ) 67 { 68 // store TextBox values string array 69 string[] values = GetTextBoxValues(); 70 71 // Record containing TextBox values to serialize 72 RecordSerializable record = new RecordSerializable(); 73 74 // determine whether TextBox account field is empty 75 if ( values[ ( int ) TextBoxIndices.ACCOUNT ] != "" ) 76 { 77 // store TextBox values in Record and serialize Record 78 try 79 { 80 // get account number value from TextBox 81 int accountNumber = Int32.Parse( 82 values[ ( int ) TextBoxIndices.ACCOUNT ] ); 83 84 // determine whether accountNumber is valid 85 if ( accountNumber > 0 ) 86 { 87 // store TextBox fields in Record 88 record.Account = accountNumber; 89 record.FirstName = values[ ( int ) TextBoxIndices.FIRST ]; 90 record.LastName = values[ ( int ) TextBoxIndices.LAST ]; 91 record.Balance = Decimal.Parse( values[ 92 ( int ) TextBoxIndices.BALANCE ] ); 93 94 // write Record to FileStream ( serialize object ) 95 formatter.Serialize( output, record ); 96 } // end if 97 else 98 { 99 // notify user if invalid account number 100 MessageBox.Show( "Invalid Account Number", "Error", 101 MessageBoxButtons.OK, MessageBoxIcon.Error ); 102 } // end else 103 } // end try 104 // notify user if error occurs in serialization 105 catch ( SerializationException ) 106 { 107 MessageBox.Show( "Error Writing to File", "Error", 108 MessageBoxButtons.OK, MessageBoxIcon.Error ); 109 } // end catch 110 // notify user if error occurs regarding parameter format 111 catch ( FormatException ) 112 { 113 MessageBox.Show( "Invalid Format", "Error", 114 MessageBoxButtons.OK, MessageBoxIcon.Error ); 115 } // end catch 116 } // end if 117 118 ClearTextBoxes(); // clear TextBox values 119 } // end method enterButton_Click 120 121 // handler for exitButton Click 122 private void exitButton_Click( object sender, EventArgs e ) 123 { 124 // determine whether file exists 125 if ( output != null ) 126 { 127 // close file 128 try 129 { 130 output.Close(); 131 } // end try 132 // notify user of error closing file 133 catch ( IOException ) 134 { 135 MessageBox.Show( "Cannot close file", "Error", 136 MessageBoxButtons.OK, MessageBoxIcon.Error ); 137 } // end catch 138 } // end if 139 140 Application.Exit(); 141 } // end method exitButton_Click 142 } // end class CreateFileForm (a) (b) (c) (d) (e) (f) (g) |
|
This program assumes that data is input correctly and in the proper record-number order. Event handler enterButton_Click (lines 66119) performs the write operation. Line 72 creates a RecordSerializable object, which is assigned values in lines 8892. Line 95 calls method Serialize to write the RecordSerializable object to the output file. Method Serialize takes the FileStream object as the first argument so that the BinaryFormatter can write its second argument to the correct file. Note that only one statement is required to write the entire object.
In the sample execution for the program in Fig. 18.14, we entered information for five accountsthe same information shown in Fig. 18.10. The program does not show how the data records actually appear in the file. Remember that we are now using binary files, which are not human readable. To verify that the file was created successfully, the next section presents a program to read the file's contents.
Reading and Deserializing Data from a Sequential Access Text File
|