Home

Object Serialization and De-Serialization

   

Introduction to Serialization

Traditional file processing consists of saving individual values one at a time. Object serialization consists of saving a whole object as one instead of its individual values. 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.

Introduction to Binary 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>]
type Brokerage ... =
    ...

Before serializing an object, you should reference the System.Runtime.Serialization.Formatters.Binary namespace. The class responsible for binary serialization is called BinaryFormatter:

type BinaryFormatter =  
    class 
        interface IRemotingFormatter 
        interface IFormatter 
    end

This class is equipped with two constructors. The default constructor is simply used to 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 signature:

abstract Serialize : 
        serializationStream:Stream * 
        graph:Object -> unit  
override Serialize : 
        serializationStream:Stream * 
        graph:Object -> unit

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:

open System
open System.IO
open System.Drawing
open System.Windows.Forms
open System.Runtime.Serialization.Formatters.Binary

[<Serializable>]
type Brokerage(shares, price) =
    let mutable s = shares
    let mutable p = price

    member this.Shares    with get() = s and set(nbr)   = s <- nbr
    member this.UnitValue with get() = p and set(value) = p <- value

    member this.Principal with get() = s * p
    member this.Commission with get() =
                            let mutable commission = 0.00
                            if p >= 0.00 && p <= 2500.00 then
                                commission <- 26.25 + (p * 0.0014)
                            if p > 2500.00 && p <= 6000.00 then
                                commission <- 45.00 + (p * 0.0054)
                            if p > 6000.00 && p <= 20000.00 then
                                commission <- 60.00 + (p * 0.0028)
                            if p > 20000.00 && p <= 50000.00 then
                                commission <- 75.00 + (p * 0.001875)
                            if p > 50000.00 && p <= 500000.00 then
                                commission <- 131.25 + (p * 0.0009)
                            if p > 500000.00 then
                                commission <- 206.25 + (p * 0.000075)
                            commission
    member this.TotalInvestment with get() = this.Principal + this.Commission

let exercise = new Form(Text = "Brokerage Company", MinimizeBox = true,
                        ClientSize = new System.Drawing.Size(220, 270))

exercise.Controls.Add(new Label(Location = new Point(18, 18),
                                 Text = "Number of Shares:", Width = 100))

let txtNumberOfShares = new TextBox(Text = "0", Width = 75,
                                    Location = new Point(122, 16))
exercise.Controls.Add(txtNumberOfShares)

exercise.Controls.Add(new Label(Location = new Point(18, 46),
                                 Text = "Price Per Share:", AutoSize = true))

let txtPricePerShare = new TextBox(Width = 75, Text = "0", Location = new Point(122, 44))
exercise.Controls.Add(txtPricePerShare)

let btnCalculate = new Button(Location = new Point(122, 72),
                              Width = 75, Text = "Calculate")

exercise.Controls.Add(new Label(Location = new Point(18, 102),
                                 AutoSize = true, Text = "Principal:"))

let txtPrincipal = new TextBox(Width = 75, Text = "0.00",
                               Location = new Point(122, 100))
exercise.Controls.Add(txtPrincipal)

exercise.Controls.Add(new Label(Location = new Point(18, 132),
                                 AutoSize = true, Text = "Commission:"))

let txtCommission = new TextBox(Width = 75, Text = "0.00",
                                Location = new Point(122, 128))
exercise.Controls.Add(txtCommission)

exercise.Controls.Add(new Label(Location = new Point(18, 160),
                                 AutoSize = true, Text = "Total Investment:"))

let txtTotalInvestment = new TextBox(Width = 75, Text = "0.00",
                                     Location = new Point(122, 156))
exercise.Controls.Add(txtTotalInvestment)

let btnCalculateClick(e) =
    let mutable principal = 0.00
    let mutable commission = 0.00
    let numberOfShares = float txtNumberOfShares.Text
    let pricePerShare  = float txtPricePerShare.Text

    let stock = new Brokerage(numberOfShares, pricePerShare)

    txtPrincipal.Text <- sprintf "%0.02f" stock.Principal
    txtCommission.Text <- sprintf "%0.02f" stock.Commission
    txtTotalInvestment.Text <- sprintf "%0.02f" stock.TotalInvestment

btnCalculate.Click.Add btnCalculateClick
exercise.Controls.Add btnCalculate

let txtFileName = new TextBox(Location = new Point(104, 192), Width = 92)
exercise.Controls.Add txtFileName

let btnSave = new Button(Location = new Point(18, 192), Text = "Save", Width = 75)
let btnSaveClick e =
    let pricePerShare  = float txtPricePerShare.Text
    let numberOfShares = float txtNumberOfShares.Text

    if String.IsNullOrEmpty(txtFileName.Text) = false then
	let bfStock : BinaryFormatter = new BinaryFormatter()
	let fsStock = new FileStream(@"C:\Resources\stocks.stk", FileMode.Create)
	
	let stock = new Brokerage(numberOfShares, pricePerShare)
	
	bfStock.Serialize(fsStock, stock)
    exercise.Close()
btnSave.Click.Add btnSaveClick
exercise.Controls.Add btnSave

let btnClose = new Button(Size = new System.Drawing.Size(183, 30),
                          Location = new Point(18, 225), Text = "Close")
let btnCloseClick e = exercise.Close()
btnClose.Click.Add(btnCloseClick)
exercise.Controls.Add(btnClose)

[<STAThread>]
[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

Here is an example of testing the program:

Introduction to Binary Serialization

 
   
 
    

De-Serialization

As serialization is the process of storing an object to a medium, the opposite, de-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 signature:

abstract Deserialize : 
        serializationStream:Stream -> Object  
override Deserialize : 
        serializationStream:Stream -> Object

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:

De-Serialization

open System
open System.IO
open System.Drawing
open System.Collections
open System.Windows.Forms
open System.Runtime.Serialization.Formatters.Binary

[<Serializable>]
type Brokerage(shares, price) =
    let mutable s = shares
    let mutable p = price

    member this.Shares    with get() = s and set(nbr)   = s <- nbr
    member this.UnitValue with get() = p and set(value) = p <- value

    member this.Principal with get() = s * p
    member this.Commission with get() =
                            let mutable commission = 0.00
                            if p >= 0.00 && p <= 2500.00 then
                                commission <- 26.25 + (p * 0.0014)
                            if p > 2500.00 && p <= 6000.00 then
                                commission <- 45.00 + (p * 0.0054)
                            if p > 6000.00 && p <= 20000.00 then
                                commission <- 60.00 + (p * 0.0028)
                            if p > 20000.00 && p <= 50000.00 then
                                commission <- 75.00 + (p * 0.001875)
                            if p > 50000.00 && p <= 500000.00 then
                                commission <- 131.25 + (p * 0.0009)
                            if p > 500000.00 then
                                commission <- 206.25 + (p * 0.000075)
                            commission
    member this.TotalInvestment with get() = this.Principal + this.Commission

let exercise = new Form(Text = "Brokerage Company", MinimizeBox = true,
                        ClientSize = new System.Drawing.Size(220, 270))

exercise.Controls.Add(new Label(Location = new Point(18, 18),
                                 Text = "Number of Shares:", Width = 100))

let txtNumberOfShares = new TextBox(Text = "0", Width = 75,
                                    Location = new Point(122, 16))
exercise.Controls.Add(txtNumberOfShares)

exercise.Controls.Add(new Label(Location = new Point(18, 46),
                                 Text = "Price Per Share:", AutoSize = true))

let txtPricePerShare = new TextBox(Width = 75, Text = "0", Location = new Point(122, 44))
exercise.Controls.Add(txtPricePerShare)

let btnCalculate = new Button(Location = new Point(122, 72),
                              Width = 75, Text = "Calculate")

exercise.Controls.Add(new Label(Location = new Point(18, 102),
                                 AutoSize = true, Text = "Principal:"))

let txtPrincipal = new TextBox(Width = 75, Text = "0.00",
                               Location = new Point(122, 100))
exercise.Controls.Add(txtPrincipal)

exercise.Controls.Add(new Label(Location = new Point(18, 132),
                                 AutoSize = true, Text = "Commission:"))

let txtCommission = new TextBox(Width = 75, Text = "0.00",
                                Location = new Point(122, 128))
exercise.Controls.Add(txtCommission)

exercise.Controls.Add(new Label(Location = new Point(18, 160),
                                 AutoSize = true, Text = "Total Investment:"))

let txtTotalInvestment = new TextBox(Width = 75, Text = "0.00",
                                     Location = new Point(122, 156))
exercise.Controls.Add(txtTotalInvestment)

let btnCalculateClick(e) =
    let mutable principal = 0.00
    let mutable commission = 0.00
    let numberOfShares = float txtNumberOfShares.Text
    let pricePerShare  = float txtPricePerShare.Text

    let stock = new Brokerage(numberOfShares, pricePerShare)

    txtPrincipal.Text <- sprintf "%0.02f" stock.Principal
    txtCommission.Text <- sprintf "%0.02f" stock.Commission
    txtTotalInvestment.Text <- sprintf "%0.02f" stock.TotalInvestment

btnCalculate.Click.Add btnCalculateClick
exercise.Controls.Add btnCalculate

let txtFileName = new TextBox(Location = new Point(76, 194), Width = 65)
exercise.Controls.Add txtFileName

let btnSave = new Button(Location = new Point(18, 192), Text = "Save", Width = 50)
let btnSaveClick e =
    let pricePerShare  = float txtPricePerShare.Text
    let numberOfShares = float txtNumberOfShares.Text

    if String.IsNullOrEmpty(txtFileName.Text) = false then
        let bfStock : BinaryFormatter = new BinaryFormatter()
        let fsStock = new FileStream(@"C:\Resources\" + txtFileName.Text + ".stk", FileMode.Create)

        let stock = new Brokerage(numberOfShares, pricePerShare)

        bfStock.Serialize(fsStock, stock)
    exercise.Close()
btnSave.Click.Add btnSaveClick
exercise.Controls.Add btnSave

let btnOpen = new Button(Location = new Point(148, 192), Text = "Open", Width = 50)
let btnOpenClick e =
    let bfStock : BinaryFormatter = new BinaryFormatter()
    let fsStock = new FileStream(@"C:\Resources\" + txtFileName.Text + ".stk", FileMode.Open)

    let stock = bfStock.Deserialize(fsStock) :?> Brokerage

    txtNumberOfShares.Text <- sprintf "%0.02f" stock.Shares
    txtPricePerShare.Text  <- sprintf "%0.02f" stock.UnitValue
    txtPrincipal.Text <- sprintf "%0.02f" stock.Principal
    txtCommission.Text <- sprintf "%0.02f" stock.Commission
    txtTotalInvestment.Text <- sprintf "%0.02f" stock.TotalInvestment

    bfStock.Serialize(fsStock, stock)
btnOpen.Click.Add btnOpenClick
exercise.Controls.Add btnOpen

let btnClose = new Button(Size = new System.Drawing.Size(180, 30),
                          Location = new Point(18, 225), Text = "Close")
let btnCloseClick e = exercise.Close()
btnClose.Click.Add btnCloseClick
exercise.Controls.Add btnClose

[<STAThread>]
[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

De-Serialization

SOAP Serialization

The .NET Framework supports another technique of serialization referred to as SOAP (which stands for Simple Object Access Protocol). This technique is related to XML but, although we haven't studied XML, you don't need to know anything about it to use SOAP serialization.

When creating the class whose objects would be serialized, mark it with the [<Serializable>] attribute. Here is an example:

[<Serializable>]
type Distribution =
    . . .

To serialize an object using SOAP, you follow the same steps we reviewed for the binary serialization with one addition: you must add a certain reference.

To support SOAP serialization, the .NET Framework provides the SoapFormatter class:

type SoapFormatter =  
    class 
        interface IRemotingFormatter 
        interface IFormatter 
    end

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. Then, you can create an object and initialize it as you see fit. Before saving it, as always, create a Stream-based object that indicates 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 signature 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.

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 signature as its equivalent of the BinaryFormatter class. The approach to use it is also the same.

Details on Serialization

 

Partial Serialization

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. After creating the class, you can declare a variable of it and serialize it, using either the binary or the SOAP approach. You can then retrieve the object and its values, using any of the techniques we learned earlier.

Implementing a Custom Serialized Class

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.

.NET Built-In Serialized Classes

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:

The Serializable attribute of a built-in 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.

 
 
   
 

Home Copyright © 2015, FunctionX Home