Serialization

In document in .NET (Page 54-65)

I/O in the .NET Framework

2.2 Streams

2.2.4 Serialization

Note: int in C# is a signed 4-byte number; thus the resultant file is exactly 4,000 bytes long.

The significant methods and properties for BinaryWriter are shown in Table 2.3.

tons on the form. Name the buttons button1 and button2, respectively.

Click on the form, and enter the following code:

C#

public enum purchaseOrderStates {

ISSUED, DELIVERED, INVOICED, PAID }

[Serializable()]

public class company {

public string name;

public string address;

public string phone;

}

[Serializable()]

public class lineItem {

public string description;

public int quantity;

public double cost;

}

[Serializable()]

public class purchaseOrder {

private purchaseOrderStates _purchaseOrderStatus;

private DateTime _issuanceDate;

private DateTime _deliveryDate;

private DateTime _invoiceDate;

private DateTime _paymentDate;

public company buyer;

public company vendor;

public string reference;

public lineItem[] items;

public purchaseOrder() {

_purchaseOrderStatus=purchaseOrderStates.ISSUED;

_issuanceDate=DateTime.Now;

}

public void recordDelivery() {

if (_purchaseOrderStatus==purchaseOrderStates.ISSUED) {

_purchaseOrderStatus=purchaseOrderStates.DELIVERED;

_deliveryDate=DateTime.Now;

} }

public void recordInvoice() {

if

(_purchaseOrderStatus==purchaseOrderStates.DELIVERED) {

_purchaseOrderStatus=purchaseOrderStates.INVOICED;

_invoiceDate=DateTime.Now;

} }

public void recordPayment() {

if (_purchaseOrderStatus==purchaseOrderStates.INVOICED) {

_purchaseOrderStatus=purchaseOrderStates.PAID;

_paymentDate=DateTime.Now;

} } }

VB.NET

Public Enum purchaseOrderStates ISSUED

DELIVERED INVOICED PAID End Enum

<Serializable()> _ Public Class company Public name As String Public address As String

Public phone As String End Class

<Serializable()> _ Public Class lineItem

Public description As String Public quantity As Integer Public cost As Double End Class

<Serializable()> _

Public Class purchaseOrder

Private _purchaseOrderStatus As purchaseOrderStates Private _issuanceDate As DateTime

Private _deliveryDate As DateTime Private _invoiceDate As DateTime Private _paymentDate As DateTime Public buyer As company

Public vendor As company Public reference As String Public items() As lineItem Public sub New()

_purchaseOrderStatus=purchaseOrderStates.ISSUED _issuanceDate=DateTime.Now

End sub

Public sub recordDelivery()

if _purchaseOrderStatus=purchaseOrderStates.ISSUED _purchaseOrderStatus=purchaseOrderStates.DELIVERED _deliveryDate=DateTime.Now

end if end sub

Public sub recordInvoice()

if _purchaseOrderStatus=purchaseOrderStates.DELIVERED _purchaseOrderStatus=purchaseOrderStates.INVOICED _invoiceDate=DateTime.Now

end if end sub

Public sub recordPayment()

if _purchaseOrderStatus=purchaseOrderStates.INVOICED

_purchaseOrderStatus=purchaseOrderStates.PAID _invoiceDate=DateTime.Now

end if end sub End Class

Note: The use of the [Serializable()] tag facilitates deep seilalization. It is possible to perform deep serialization without this tag by using surrogates. A surrogate is where the a class implements ISerializationSurrogate, and is passed to the AddSurrogate method of a SurrogateSelector object. The

SurrogateSelector property of the formatter is then set equal to this object prior to serialization.

The _purchaseOrderStatus variable is private and can only be modified by recordDelivery(), recordInvoice(), and recordPayment(). This ensures that a bug elsewhere in the code will not cause undelivered goods to be paid for (i.e., _purchaseOrderStatus cannot change directly from

ISSUED to PAID). Similarly, the date recording is encapsulated within the object and cannot be externally manipulated.

To place a purchase order on a stream (either to disk or to the network), you could write each value one after the other as text, separated by commas, and have the receiver parse out the values and re-create the object; however, there is an easier way: serialization.

To write the object to a stream and save the object to disk, you could use the following code:

C#

private void button1_Click(object sender, System.EventArgs e) {

company Vendor = new company();

company Buyer = new company();

lineItem Goods = new lineItem();

purchaseOrder po = new purchaseOrder();

Vendor.name = "Acme Inc.";

Buyer.name = "Wiley E. Coyote";

Goods.description = "anti-RoadRunner cannon";

Goods.quantity = 1;

Goods.cost = 599.99;

po.items = new lineItem[1];

po.items[0] = Goods;

po.buyer = Buyer;

po.vendor = Vendor;

SoapFormatter sf = new SoapFormatter();

FileStream fs = File.Create("C:\\po.xml");

sf.Serialize(fs,po);

fs.Close();

}

VB.NET

Private Sub Button1_Click(ByVal sender As Object, ByVal e As _ System.EventArgs) Handles Button1.Click

Dim Vendor As company = New company() Dim Buyer As company = New company() Dim Goods As lineItem = New lineItem()

Dim po As purchaseOrder = New purchaseOrder() Vendor.name = "Acme Inc."

Buyer.name = "Wiley E. Coyote"

Goods.description = "anti-RoadRunner cannon"

Goods.quantity = 1 Goods.cost = 599.99

po.items = New lineItem(1) {}

po.items(0) = Goods po.buyer = Buyer po.vendor = Vendor

Dim sf As SoapFormatter = New SoapFormatter() Dim fs As FileStream = File.Create("C:\po.xml") sf.Serialize(fs,po)

fs.Close() End Sub

To read the object back into memory, we can deserialize it thus:

C#

private void button2_Click(object sender, System.EventArgs e) {

SoapFormatter sf = new SoapFormatter();

FileStream fs = File.OpenRead("C:\\po.xml");

purchaseOrder po = (purchaseOrder)sf.Deserialize(fs);

fs.Close();

MessageBox.Show("Customer is " + po.buyer.name);

}

VB.NET

Private Sub button2_Click(ByVal sender As Object, ByVal e As_

System.EventArgs) Handles Button2.Click

Dim sf As SoapFormatter = New SoapFormatter() Dim fs As FileStream = File.OpenRead("C:\po.xml") Dim po As purchaseOrder = CType(sf.Deserialize(fs),_

purchaseOrder) fs.Close()

MessageBox.Show("Customer is " + po.buyer.name) End Sub

Before this code will work, you will need an assembly reference for

SoapFormatter. This is done by clicking Project→→Add Reference and select-→→ ing System.Runtime.Serialization.Formatters.Soap, then adding this line to the top of the code:

C#

using System.IO;

using System.Runtime.Serialization.Formatters.Soap;

VB.NET

imports System.IO

imports System.Runtime.Serialization.Formatters.Soap

To test this application, run it from Visual Studio .NET. Press the Serial-ize button and then the DeserialSerial-ize button. You will see the message “Cus-tomer is Wiley E. Coyote,” as depicted in Figure 2.3.

If you open the file C:\PO.XML, you will see a human-readable represen-tation of the object, as shown in Figure 2.4. This format is known as simple object access protocol (SOAP) and is very portable between platforms (e.g., WebSphere for UNIX can read it).

Note: The constructor is not called during deserialization. In the above example, you will see that the issue date does not change when the object is re-created from disk.

The significant methods and properties for SoapFormatter are shown in Table 2.4.

Figure 2.3 Serializing .NET classes.

Figure 2.4 XML view of a serialized object.

Serializing to binary

SOAP formatting may be very impressive, but it is far from compact and may be quite bandwidth consuming if sent over a slow network. We can therefore use the native binary format to store the array by substituting

SoapFormatter with BinaryFormatter in the above example thus:

C#

BinaryFormatter bf = new BinaryFormatter();

FileStream fs = File.Create("C:\\po.bin");

bf.Serialize(fs,po);

fs.Close();

VB.NET

Dim bf As BinaryFormatter = New BinaryFormatter() Dim fs As FileStream = File.Create("C:\po.bin") bf.Serialize(fs,po)

fs.Close()

And deserialize with this code:

C#

BinaryFormatter bf = new BinaryFormatter();

FileStream fs = File.OpenRead("C:\\po.bin");

Table 2.4 Significant members of SoapFormatter . Method or Property Purpose

Constructor Initializes a new instance of the SoapFormatter class. It may be invoked without any parameters.

Deserialize Deserializes a stream into an object graph. It may be invoked thus: Deserialize(Stream).

Serialize Serializes an object or graph of connected objects. It may be invoked thus: Serialize(Stream, object).

AssemblyFormat Gets or sets the format in which assembly names are serialized.

Returns FormatterAssemblyStyle.

TypeFormat Gets or sets the format in which type descriptions are laid out in the serialized stream. Returns FormatterTypeStyle.

TopObject Gets or sets the ISoapMessage into which the SOAP top object is deserialized. Returns ISoapMessage.

purchaseOrder po = (purchaseOrder)bf.Deserialize(fs);

fs.Close();

VB.NET

Dim bf As BinaryFormatter = New BinaryFormatter() Dim fs As FileStream = File.OpenRead("C:\po.bin") Dim po As purchaseOrder = CType(bf.Deserialize(fs), _ purchaseOrder)

fs.Close()

When substituting the SoapFormatter with the BinaryFormatter, a ref-erence to System.Runtime.Serialization.Formatters.Soap is no longer required. Instead, the Formatters.Binary namespace is required; it can be added by inserting this line to the top of the code:

C#

using System.Runtime.Serialization.Formatters.Binary;

VB.NET

imports System.Runtime.Serialization.Formatters.Binary

This produces a file that is considerably smaller than the previous SOAP version. The resulting file is not human readable, and it is unfeasible to port to other platforms.

Note: Binary representations, although difficult to read, are not a secure way of protecting sensitive data.

The BinaryFormatter object is programatically identical to the Soap-Formatter object, except that it does not support the topObject method.

Shallow serialization

Whenever an object is serialized without its private and protected members, this is known as shallow serialization. This may cause problems as a result of inaccurate copies of objects; for instance, in the purchase order application, users would find their orders reverting from PAID to ISSUED. Furthermore, shallow serialization cannot resolve circular references within objects. For instance, if a BookCatalog class has a member of type Book, and the Book

class has a member of type BookCatalog, then neither object can be serial-ized shallowly.

One benefit of shallow serialization is that it uses XML schema defini-tion (XSD) to define types. The XSD standard ensures faithful representa-tions on other platforms. The SOAP formatter, as used in deep serialization, uses the CLR-type system and is not standardized across non-.NET platforms.

Code for shallow serialization can be seen by the use of code similar to the following:

C#

XmlSerializer xs = new XmlSerializer(po.GetType());

FileStream fs = File.Create("C:\\po.xml");

xs.Serialize(fs,po);

fs.Close();

VB.NET

Dim xs As XmlSerializer = New XmlSerializer(po.GetType()) Dim fs As FileStream = File.Create("C:\po.xml")

xs.Serialize(fs,po) fs.Close()

Shallow deserialization is performed with the following code:

C#

purchaseOrder po = new purchaseOrder();

XmlSerializer xs = new XmlSerializer(po.GetType());

FileStream fs = File.OpenRead("C:\\po.xml");

po = (purchaseOrder)xs.Deserialize(fs);

fs.Close();

MessageBox.Show("Customer is " + po.buyer.name);

VB.NET

Dim po As purchaseOrder = New purchaseOrder()

Dim xs As XmlSerializer = New XmlSerializer(po.GetType()) Dim fs As FileStream = File.OpenRead("C:\po.xml")

po = CType(xs.Deserialize(fs), purchaseOrder) fs.Close()

MessageBox.Show("Customer is " + po.buyer.name)

The following namespace is required for the XmlSerializer object:

C#

using System.Xml.Serialization;

VB.NET

imports System.Xml.Serialization

The significant methods and properties for XMLSerializer are shown in Table 2.5.

In document in .NET (Page 54-65)