DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 5.4 Serializing Data

Problem

You need to serialize the contents of a DataSet so that you can store the data on a disk or transfer it across a network.

Solution

You can serialize a DataSet into XML, binary, or SOAP formats and save the serialized DataSet to a stream (such as a file or network stream).

The sample code creates a DataSet containing the Orders and Order Details tables from Northwind and a relation between the two tables. A file stream is created and the DataSet is serialized to the stream using a specified format.

The C# code is shown in Example 5-4.

Example 5-4. File: SerializeForm.cs
// Namespaces, variables, and constants
using System;
using System.Configuration;
using System.Windows.Forms;
using System.IO;
using System.Data;
using System.Data.SqlClient;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Xml.Serialization;

// Table name constants
private const String ORDERS_TABLE        = "Orders";
private const String ORDERDETAILS_TABLE  = "OrderDetails";

// Relation name constants
private const String ORDERS_ORDERDETAILS_RELATION =
    "Orders_OrderDetails_Relation";

// Field name constants
private const String ORDERID_FIELD       = "OrderID";

private SaveFileDialog sfd;

//  . . . 

private void goButton_Click(object sender, System.EventArgs e)
{
    DataSet ds = new DataSet( );
    
    SqlDataAdapter da;

    // Fill the Order table and add it to the DataSet.
    da = new SqlDataAdapter("SELECT * FROM Orders",
        ConfigurationSettings.AppSettings["Sql_ConnectString"]);
    DataTable orderTable = new DataTable(ORDERS_TABLE);
    da.FillSchema(orderTable, SchemaType.Source);
    da.Fill(orderTable);
    ds.Tables.Add(orderTable);

    // Fill the OrderDetails table and add it to the DataSet.
    da = new SqlDataAdapter("SELECT * FROM [Order Details]",
        ConfigurationSettings.AppSettings["Sql_ConnectString"]);
    DataTable orderDetailTable = new DataTable(ORDERDETAILS_TABLE);
    da.FillSchema(orderDetailTable, SchemaType.Source);
    da.Fill(orderDetailTable);
    ds.Tables.Add(orderDetailTable);

    // Create a relation between the tables.
    ds.Relations.Add(ORDERS_ORDERDETAILS_RELATION,
        ds.Tables[ORDERS_TABLE].Columns[ORDERID_FIELD],
        ds.Tables[ORDERDETAILS_TABLE].Columns[ORDERID_FIELD],
        true);

    // Create and open the stream for serializing.
    Stream stream = null;
    try
    {
        stream = File.Open(fileNameTextBox.Text, FileMode.Create,
            FileAccess.Write);
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.Message, "Serializing Data",
            MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }

    // Serialize.
    if (xmlWriteRadioButton.Checked)
    {
        ds.WriteXml(stream, XmlWriteMode.WriteSchema);
    }
    else if (xmlSerializerRadioButton.Checked)
    {
        XmlSerializer xs = new XmlSerializer(typeof(DataSet));
        xs.Serialize(stream, ds);
    }
    else if(soapRadioButton.Checked)
    {
        SoapFormatter sf = new SoapFormatter( );
        sf.Serialize(stream, ds);
    }
    else if(binaryRadioButton.Checked)
    {
        BinaryFormatter bf = new BinaryFormatter( );
        bf.Serialize(stream, ds);
    }

    stream.Close( );

    MessageBox.Show("Serialization complete.", "Serializing Data",
        MessageBoxButtons.OK, MessageBoxIcon.Information);
}

private void fileDialogButton_Click(object sender, System.EventArgs e)
{
    // File dialog to save file
    if(xmlWriteRadioButton.Checked || xmlSerializerRadioButton.Checked)
        sfd.Filter = "XML files (*.xml)|*.xml";
    else if(soapRadioButton.Checked)
        sfd.Filter = "SOAP files (*.soap)|*.soap";
    else if(binaryRadioButton.Checked)
        sfd.Filter = "Binary files (*.bin)|*.bin";

    sfd.Filter += "|All files (*.*)|*.*";
    sfd.FilterIndex = 0;

    if (sfd.ShowDialog( ) == DialogResult.OK)
        fileNameTextBox.Text = sfd.FileName;
}

Discussion

Serialization converts an object into a stream of data that can be transported or saved as a file. Deserialization reconstructs the original object from the file.

The most basic data serialization is done by writing the contents of the DataSet object to XML using the WriteXml( ) or GetXml( ) method. The contents are then deserialized with the ReadXml( ) method. These methods, unlike the others shown in this solution, are limited to serializing and deserializing DataSet objects and provide little control over serialization format.

The XmlSerializer class serializes and deserializes objects into XML classes. It performs shallow serialization: only the read-write property values of the class are serialized, not the underlying data. The XML stream generated by the XmlSerializer class is compliant with the World Wide Web Consortium (http://www.w3.org) XML Schema Definition (XSD) language 1.0 recommendations. The XmlSerializer object can serialize to any object that inherits from System.IO.Stream. When constructing the XmlSerializer object, you must specify the type of object that can be serialized by the instance.

You can also use the XmlSerializer class to serialize an object into a SOAP XML stream that conforms to Section 5 of the World Wide Web Consortium document "Simple Object Access Protocol (SOAP) 1.1." To do this, use the overloaded constructor that accepts the XmlTypeMapping argument. For more information, see the topic "XmlSerializer Constructor" in the MSDN library.

The IFormatter interface provides functionality for formatting serialized objects. The class to be serialized must be marked with the SerializableAttribute attribute; otherwise, a SerializationException will be raised. A class can implement the ISerializable interface to override the default serialization behavior.

The System.Runtime.Serialization.Formatter class provides base functionality for the serialization formatters:

System.Runtime.Serialization.Formatters.Binary

This namespace contains the BinaryFormatter class that can serialize and deserialize objects in binary format.

System.Runtime.Serialization.Formatters.Soap

This namespace contains the SoapFormatter classes that can serialize and deserialize objects in SOAP format.

The BinaryFormatter and SoapFormatter both perform deep serialization: the values in the object's variables are serialized. If the object holds references to other objects, they will be serialized as well. The NonSerializedAttribute attribute can exclude a variable from the serialization process.

Both the BinaryFormatter and SoapFormatter implement the IFormatter and IRemotingFormatter interfaces, which provide functionality for formatting serialized objects and for sending and receiving remote procedure calls (RPC), respectively. The methods for serialization and deserialization in both interfaces are called Serialize( ) and Deserialize( ). Overloaded versions determine whether the call is a remote call.

The Serialize( ) method of the IFormatter interface serializes the object to a Stream object. This includes all classes that have the base class System.IO.Stream, such as:

  • System.IO.BufferedStream

  • System.IO.FileStream

  • System.IO.MemoryStream

  • System.Net.Sockets.NetworkStream

  • System.Security.Cryptography.CryptoStream

Once the serialization object has been created, serialization is accomplished by calling the Serialize( ) method of the serializing object with arguments referencing the destination stream and the object to be serialized.

The Deserialize( ) method of the IFormatter interface deserializes the specified Stream object to recreate the object graph that was previously serialized. For more information about deserializing data, see Recipe 5.5.

    [ Team LiB ] Previous Section Next Section