18.9. 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 RecordSerializable 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 (from namespace System.Runtime.Serialization). The remainder of class RecordSerializable is identical to class Record (Fig. 18.8). Figure 18.13. RecordSerializable class for serializable objects. 1 ' Fig. 18.13: RecordSerializable.vb 2 ' Serializable class that represents a data record. 3 4 < Serializable()> 5 Public Class RecordSerializable 6 Private accountValue As Integer 7 Private firstNameValue As String 8 Private lastNameValue As String 9 Private balanceValue As Decimal 10 11 ' default constructor sets members to default values 12 Public Sub New() 13 MyClass.New(0, "", "", 0D) 14 End Sub ' New 15 16 ' overloaded constructor sets members to parameter values 17 Public Sub New(ByVal account As Integer, ByVal firstName As String, _ 18 ByVal lastName As String, ByVal balance As Decimal) 19 20 account = account 21 firstNameValue = firstName 22 lastNameValue = lastName 23 balanceValue = balance 24 End Sub ' New 25 26 ' property Account 27 Public Property Account() As Integer 28 Get 29 Return accountValue 30 End Get 31 Set(ByVal value As Integer) 32 accountValue = value 33 End Set 34 End Property ' Account 35 36 ' property FirstName 37 Public Property FirstName() As String 38 Get 39 Return firstNameValue 40 End Get 41 Set(ByVal value As String) 42 firstNameValue = value 43 End Set 44 End Property ' FirstName 45 46 ' property LastName 47 Public Property LastName() As String 48 Get 49 Return lastNameValue 50 End Get 51 Set(ByVal value As String) 52 lastNameValue = value 53 End Set 54 End Property ' LastName 55 56 ' property Balance 57 Public Property Balance() As Decimal 58 Get 59 Return balanceValue 60 End Get 61 Set(ByVal value As Decimal) 62 balanceValue = value 63 End Set 64 End Property ' Balance 65 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 10 creates a BinaryFormatter for writing serialized objects. Lines 3839 open the FileStream to which this program writes the serialized objects. The String argument fileName 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: FrmCreateFile.vb 2 ' Creating a sequential-access file using serialization. 3 Imports System.IO 4 Imports System.Runtime.Serialization.Formatters.Binary 5 Imports System.Runtime.Serialization 6 Imports BankLibrary 7 8 Public Class FrmCreateFile 9 ' object for serializing Records in binary format 10 Private formatter As New BinaryFormatter() 11 Private output As FileStream ' stream for writing to a file 12 13 ' handler for btnSave_Click 14 Private Sub btnSave_Click(ByVal sender As System.Object, _ 15 ByVal e As System.EventArgs) Handles btnSave.Click 16 ' create dialog box enabling user to save file 17 Dim fileChooser As New SaveFileDialog() 18 Dim result As DialogResult = fileChooser.ShowDialog() 19 Dim fileName As String ' name of file to save data 20 21 fileChooser.CheckFileExists = False ' allow user to create file 22 23 ' exit event handler if user clicked "Cancel" 24 If result = Windows.Forms.DialogResult.Cancel Then 25 Return 26 End If 27 28 fileName = fileChooser.FileName ' get specified file name 29 30 ' show error if user specified invalid file 31 If fileName = "" Or fileName Is Nothing Then 32 MessageBox.Show("Invalid File Name", "Error", _ 33 MessageBoxButtons.OK, MessageBoxIcon.Error) 34 Else 35 ' save file via FileStream if user specified valid file 36 Try 37 ' open file with write access 38 output = New FileStream( _ 39 fileName, FileMode.OpenOrCreate, FileAccess.Write) 40 41 ' disable Save button and enable Enter button 42 btnSave.Enabled = False 43 btnEnter.Enabled = True 44 ' handle exception if there is a problem opening the file 45 Catch ex As IOException 46 ' notify user if file does not exist 47 MessageBox.Show("Error opening file", "Error", _ 48 MessageBoxButtons.OK, MessageBoxIcon.Error) 49 End Try 50 End If 51 End Sub ' btnSave_Click 52 53 ' handler for btnEnter_Click 54 Private Sub btnEnter_Click(ByVal sender As System.Object, _ 55 ByVal e As System.EventArgs) Handles btnEnter.Click 56 ' store TextBox values string array 57 Dim values As String() = GetTextBoxValues() 58 59 ' Record containing TextBox values to serialize 60 Dim record As New RecordSerializable() 61 62 ' determine whether TextBox account field is empty 63 If values(TextBoxIndices.ACCOUNT) <> "" Then 64 ' store TextBox values in Record and serialize Record 65 Try 66 ' get account number value from TextBox 67 Dim accountNumber As Integer = _ 68 Int32.Parse(values(TextBoxIndices.ACCOUNT)) 69 70 ' determine whether accountNumber is valid 71 If accountNumber > 0 Then 72 ' store TextBox fields in Record 73 record.Account = accountNumber 74 record.FirstName = _ 75 values(TextBoxIndices.FIRST) 76 record.LastName = _ 77 values(TextBoxIndices.LAST) 78 record.Balance = _ 79 Decimal.Parse(values(TextBoxIndices.BALANCE)) 80 81 ' write Record to FileStream ( serialize object ) 82 formatter.Serialize(output, record) 83 Else 84 ' notify user if invalid account number 85 MessageBox.Show("Invalid Account Number", "Error" , _ 86 MessageBoxButtons.OK, MessageBoxIcon.Error) 87 End If 88 ' notify user if error occurs in serialization 89 Catch ex As SerializationException 90 MessageBox.Show("Error Writing to File", "Error" , _ 91 MessageBoxButtons.OK, MessageBoxIcon.Error) 92 ' notify user if error occurs regarding parameter format 93 Catch ex As FormatException 94 MessageBox.Show("Invalid Format", "Error", _ 95 MessageBoxButtons.OK, MessageBoxIcon.Error) 96 End Try 97 End If 98 99 ClearTextBoxes() ' clear TextBox values 100 End Sub ' btnEnter_Click 101 102 ' handler for btnSave_Click 103 Private Sub btnExit_Click(ByVal sender As System.Object, _ 104 ByVal e As System.EventArgs) Handles btnExit.Click 105 ' determine whether file exists 106 If output IsNot Nothing Then 107 ' close file 108 Try 109 output.Close() 110 ' notify user of error closing file 111 Catch ex As IOException 112 MessageBox.Show("Cannot close file", "Error" , _ 113 MessageBoxButtons.OK, MessageBoxIcon.Error) 114 End Try 115 End If 116 117 Application.Exit() 118 End Sub ' btnEnter_Click 119 End Class ' FrmCreateFile
(a)
(b)
(c)
(d)
(e)
(f)
(g) | Common Programming Error 18.2 | It is a logic error to open an existing file for output when the user wishes to preserve the filethe original file's contents will be lost. |
This program assumes that data is input correctly and in the proper record-number order. Event handler btnEnter_Click (lines 54100) performs the write operation. Line 60 creates a RecordSerializable object, which is assigned values in lines 7379. Line 82 calls method Serialize to write the RecordSerializable object to the output file. Method Serialize receives as its first argument the FileStream object into which the BinaryFormatter writes its second argument. 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 and deserialize the objects. |