In This Article

Serialization

The ActiproSoftware.Windows.Serialization namespace contains several classes that are helpful for serializing objects to XAML and persisting hierarchies of data (such as for control layouts) in XML.

Using XamlSerializer to Save/Load Objects from XAML

The XamlSerializer class provides helper methods for easily serializing objects to and deserializing objects from XAML.

The XamlSerializer class has these important members:

Member Description
LoadFromFile Method Deserializes an object from the specified file.
LoadFromStream Method Deserializes an object from the specified Stream.
LoadFromString Method Deserializes an object from the specified XAML string.
LoadFromXmlReader Method Deserializes an object from the specified XmlReader.
SaveToFile Method Serializes the specified object to XAML within a file.
SaveToStream Method Serializes the specified object to XAML within a Stream.
SaveToString Method Serializes the specified object to a XAML string.
SaveToXmlWriter Method Serializes the specified object to XAML by using an XmlWriter.

This sample code shows how to use the SaveToString method to serialize an object named myobject to XAML:

string xaml = new XamlSerializer().SaveToString(myobject);

This sample code shows how to use the LoadFromString method to later deserialize the from the XAML string:

object myobject = new XamlSerializer().LoadFromString(xaml);

Saving/Loading Object Hierarchies from XML

There are countless cases where it is useful to persist a hierarchy of data to XML that can be saved and reloaded later.

One example of this is storing the layout of a customizable control such as a NavigationBar, where the end user can reorder and show/hide panes. The layout needs to be saved and restored between application sessions so that their customizations are kept intact.

The Shared Library has a complete framework for supporting easy serialization and deserialization of a hierarchy of objects (such as layout data) to XML. The framework uses a standard XmlSerializer to do the actual conversion to and from XML, but the framework has numerous extra features, such as that ability to save/load to various targets like Streams, strings, etc. It also can raise an event any time an object is serialized or deserialized, allowing you to easily store and retrieve custom data anywhere in the serialized output.

Creating the Root Serializer

The first step in creating a serializable hierarchy is making the root serializer class.

This class should inherit the base generic XmlSerializerBase<T, U> class. The first type parameter indicates the Type of target object represented by the second type parameter's object Type. The second type parameter indicates the Type of the root object that will be serialized and must inherit XmlObjectBase.

For instance NavigationBar's layout serialization class, NavigationBarLayoutSerializer, is defined as:

public class NavigationBarLayoutSerializer : XmlSerializerBase<NavigationBar, XmlNavigationBarLayout> { ... }

The type NavigationBar is the first type parameter since it is the "real" object affected by the layout, and the type XmlNavigationBarLayout is the second type parameter since it is the root object that is serialized.

Next there are three methods to override. First, override GetXmlSerializer to return a standard XmlSerializer that specifies the Types that will be serialized/deserialized.

Second, override ApplyTo with code that examines the RootNode property value and updates the passed object (like a NavigationBar instance).

Third, override CreateRootNodeFor to create the root XML node that will be serialized (like a XmlNavigationBarLayout) for the passed object.

Creating the Serializable Objects

Next, create the objects that will be part of the hierarchy to serialize. The objects must inherit XmlObjectBase. This base class provides several helper methods like converting Point, Size, and Rect objects to and from strings. It also defines a Tag property, useful for persisting custom data via the serialization and deserialization events that are raised (see below).

The serializable objects should start with Xml as a naming convention and be located within a Serialization child namespace.

Do use the standard XML serialization attributes on the types and members you define, such as XmlType, XmlElement, XmlAttribute, etc. These attributes help control the XML output during serialization. Remember that all public properties will be serialized.

Serializing and Deserializing

The XmlSerializerBase<T, U> class defines a number of methods that have the similar definitions as those described above for XamlSerializer. This means that you can save/load from a Stream, string, file, etc. in one line of code.

A difference is that many of the XmlSerializerBase<T, U> methods get and set its RootNode property, which stores the root XmlObjectBase object that is serialized and deserialized.

This sample code shows how to save a NavigationBar layout to an XML string:

string layout = new NavigationBarLayoutSerializer().SaveToString(navBar);

This sample code shows how to load a NavigationBar layout from the XML string:

new NavigationBarLayoutSerializer().LoadFromString(layout, navBar);

Serializing/Deserializing Custom Data

A key benefit of using the Shared Library's XML serialization framework is that custom data can be inserted anywhere within the serialized data via the handling of an event in the application that calls for the serialization.

This is particularly useful when the developer calling the serialization code didn't write it and doesn't have access to change its code.

To enable insertion of custom data, create an event handler that accepts an ItemSerializationEventArgs argument. Then attach the event handler to the XmlSerializerBase<T, U>.ObjectSerialized event. When you go to serialize data, your method will be called after each object in the hierarchy is serialized.

The Node property in the event arguments provides the XmlObjectBase that is being serialized, and that represents the serializable data for the object indicated in the Item property. You can set the Tag property of the Node to save any custom data with the serialized data.

Deserialization is the same process. Create an event handler with the same argument type and attach it to the XmlSerializerBase<T, U>.ObjectDeserialized event. Your method will be called, passing the same sort of arguments, whenever an object is deserialized. So here you can read your custom data back in.

Note

For a good example of serializing custom data, see the "NavigationBar Layout Save/Load" QuickStart.

Custom Data Types

Sometimes you may be using custom data types in the data that is serialized and deserialized. The XML serializer needs to know about custom data types so that it can properly map XML tags to .NET types. The XmlSerializerBase<T, U> has a CustomTypes property that allows you to specify custom data types, thereby preventing any exceptions such as:

The type <YourTypeHere> was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.

This sample code shows how to register a CustomData type with the serializer, thereby preventing the above exception when performing serialization:

serializer.CustomTypes.Add(typeof(CustomData));