Serialized Objects


Applications, as you have seen, often need to store data on a hard disk. So far in this chapter, you've looked at constructing text and data files piece by piece, but often that isn't the most convenient way of doing things. Sometimes it's better to store data in the form that it is used in, namely objects.

the .NET Framework provides the infrastructure to serialize objects in the the System.Runtime.Serialization and System.Runtime.Serialization.Formatters namespaces, with specific classes implementing this infrastructure in namespaces below the latter. There are two implementations available to you in the Framework:

  • System.Runtime.Serialization.Formatters.Binary: This namespace contains the class BinaryFormatter, which is capable of serializing objects into binary data, and vice versa.

  • System.Runtime.Serialization.Formatters.Soap: This namespace contains the class SoapFormatter, which is capable of serializing objects into SOAP format XML data, and vice versa.

In this chapter, you just look at BinaryFormatter, since you have yet to learn about XML data. However, since these classes implement the IFormatter interface much of the discussion applies equally to both.

The IFormatter interface provides the methods shown in the following table.

Method

Description

void Serialize(Stream stream, object source)

Serializes source into stream

object Deserialize(Stream stream)

Deserializes the data in stream and returns the resultant object

Importantly, and conveniently for this chapter, these methods work with streams. This makes it easy to tie these methods into the file access techniques you've already seen in this chapter — you can use FileStream objects.

So, serializing using BinaryFormatter is as simple as this:

 IFormatter serializer = new BinaryFormatter(); serializer.Serialize(myStream, myObject); 

Deserializing is equally easy:

 IFormatter serializer = new BinaryFormatter(); MyObjectType myNewObject = serializer.Deserialize(myStream) as MyObjectType; 

Obviously, you need streams and objects to work with, but the preceding holds true for pretty much all circumstances. In the following Try It Out, you'll se this working in practice.

Try It Out – Object Serialization

image from book
  1. Create a new console application called ObjectStore in the directory C:\BegVCSharp\ Chapter22.

  2. Add a new class called Product to the project, and modify the code as follows:

    namespace ObjectStore {    public class Product    { public long Id; public string Name; public double Price; [NonSerialized] string Notes; public Product(long id, string name, double price, string notes) { Id = id; Name = name; Price = price; Notes = notes; } public override string ToString() { return string.Format("{0}: {1} (${2:F2}) {3}", Id, Name, Price, Notes); }    } }

  3. Place the following lines of code near the top of Program.cs. You need to import the System.IO namespace for your file handling and the other namespaces for serialization:

    using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Runtime.Serialization;  using System.Runtime.Serialization.Formatters.Binary; 

  4. Now add the following code to the Main() method in Program.cs:

    static void Main(string[] args) { try { // Create products. List<Product> products = new List<Product>(); products.Add(new Product(1, "Spiky Pung", 1000.0, "Good stuff.")); products.Add(new Product(2, "Gloop Galloop Soup", 25.0, "Tasty.")); products.Add(new Product(4, "Hat Sauce", 12.0, "One for the kids.")); Console.WriteLine("Products to save:"); foreach (Product product in products) { Console.WriteLine(product); } Console.WriteLine(); // Get serializer. IFormatter serializer = new BinaryFormatter(); // Serialize products. FileStream saveFile = new FileStream("Products.bin", FileMode.Create, FileAccess.Write); serializer.Serialize(saveFile, products); saveFile.Close(); // Deserialize products. FileStream loadFile = new FileStream("Products.bin", FileMode.Open, FileAccess.Read); List<Product> savedProducts = serializer.Deserialize(loadFile) as List<Product>; loadFile.Close(); Console.WriteLine("Products loaded:"); foreach (Product product in savedProducts) { Console.WriteLine(product); } } catch (SerializationException e) { Console.WriteLine("A serialization exception has been thrown!"); Console.WriteLine(e.Message); } catch (IOException e) { Console.WriteLine("An IO exception has been thrown!"); Console.WriteLine(e.ToString()); } Console.ReadKey(); }

  5. Run the application. The result is shown in Figure 22-9.

    image from book
    Figure 22-9

  6. Modify the code in Product.cs as follows:

    namespace ObjectStore { [Serializable]    public class Product    {       ...    }
  7. Run the application again. The result is shown in Figure 22-10.

    image from book
    Figure 22-10

  8. Open Products.bin in Notepad. The text is shown in Figure 22-11.

    image from book
    Figure 22-11

How It Works

In this example, you have created a collection of Product objects, saved the collection to disk, then reloaded it. The first time you ran the application, though, an exception was thrown, because the Product object was not marked as serializable.

the .NET Framework forces you to mark objects as serializable to enable them to be serialized. There are a number of reasons for this, including:

  • Some objects don't serialize very well. They may require references to local data that only exists while they are in memory, for example.

  • Some objects might contain sensitive data that you wouldn't want to be saved in an insecure way or transferred to another process.

As you saw in the example, marking an object as serializable is a matter of moments, using the Serializable attribute:

namespace ObjectStore { [Serializable]    public class Product    {       ...    }

An important point to note here is that this attribute is not inherited by derived classes. It must be applied to each and every class that you want to be able to serialize. Also, it is worth pointing out that the List<T> class you used to generate a collection of Product objects has this attribute — otherwise, applying it to Product wouldn't have helped to make the collection serializable.

When the products collection was successfully serialized and deserialized (on the second attempt), another important fact came to light. Only the Id, Name, and Price fields were reconstituted. This is because of another attribute being used, NonSerialized:

 [NonSerialized] string Notes;

Any member can be marked with this attribute and will not be saved with other members. This can be useful, for example, if just one field or property contains sensitive data.

You also looked at the resultant saved data in the example. Note that some of the data here is human- readable, which may not be what you desire — or expect. the BinaryFormatter class makes no serious attempt to shield your data from prying eyes. Of course, since you are using streams it is a relatively easy act to intercept the data as it is saved to disk or loaded and apply your own obfuscating or encryption algorithms. The same applies to compression — using the techniques from the last section you could quite easily compress object data as it is saved to disk.

There is a lot more to the subject of serialization, but you've covered enough information to get the basics. One of the more advanced techniques that you might like to investigate is custom serialization using the ISerializable interface, which enables you to customize exactly what data is serialized. This can be important, for example, when upgrading classes subsequent to release. Changing the members exposed to serialization can cause existing saved data to become unreadable, unless you provide your own logic to save and retrieve data.

image from book




Beginning Visual C# 2005
Beginning Visual C#supAND#174;/sup 2005
ISBN: B000N7ETVG
EAN: N/A
Year: 2005
Pages: 278

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net