Framework / Serialization / DOM Serialization
DOM Serialization

DOM serialization allows you to save / load the state of any NNode derived object to XML or Binary file or stream.

 Saving Node State
The following code snippet shows how to save the state of "someNode" to a stream in binary format:
Saving Node State to a Stream
Copy Code
NDomNodeSerializer serializer = new NDomNodeSerializer();
serializer.SaveToStream(new NNode[] { someNode }, stream, ENPersistencyFormat.Binary);

Similarly saving a node state to a file is achieved with the following code:

Saving Node State to a File
Copy Code
NDomNodeSerializer serializer = new NDomNodeSerializer();
serializer.SaveToFile(new NNode[] { someNode }, "c:\\someFile.bin", ENPersistencyFormat.Binary);
 Loading Node State

In order to load a node which was previously persisted in a stream or file you must use the NDomNodeDeserializer:

Loading Node State From a Stream
Copy Code
NDomNodeDeserializer deserializer = new NDomNodeDeserializer();
NNode someNode = deserializer.LoadFromStream(stream, ENPersistencyFormat.Binary)[0];
Loading Node State From a File
Copy Code
NDomNodeDeserializer deserializer = new NDomNodeDeserializer();
NNode someNode = deserializer.LoadFromFile("c:\\someFile.bin", ENPersistencyFormat.Binary)[0];

 

 Excluding Node Properties From Serializaton 

By default all properties and children declared for a node are serializable. If you wish to exclude a property from serialization you must mark it as non serializable:

Excluding Properties from DOM Serialization
Copy Code
someProperty.SetSerializable(false);
 Implementing Custom Serializable Objects

When a property type is not an object derived from NNode or a primitive type (double, char, string etc.) you must implement the INDeeplyClonable and INDomCustomSerializable interfaces in order for this property to be registered. The following code snippet shows how to create a simple struct that can be assigned as a property type in a NNode object:

Excluding Properties from DOM Serialization
Copy Code
public struct NMyPoint : INDeeplyCloneable, INDomCustomSerializable
{
    public NMyPoint(double x, double y)
    {
        X = x;
        Y = y;
    }

    // INDeeplyCloneable Members
    public object DeepClone()
    {
        return new NMyPoint(X, Y);
    }

    // INDomCustomSerializable Members
    public void Serialize(NPropertyBag propertyBag)
    {
        propertyBag.SetDoubleValue("X", X);
        propertyBag.SetDoubleValue("Y", Y);
    }

    public void Deserialize(NPropertyBag propertyBag)
    {
        X = propertyBag.GetDoubleValue("X");
        Y = propertyBag.GetDoubleValue("Y");
    }

    // Fields
    public double X;
    public double Y;
}

The INDomCustomSerializable interface declares two methods Serialize and Deserialize, which are called during serialization and deserialization respectively. Both have a single property bag argument that provides methods for reading and writing of all primitive types and arrays.

All classes that implement the INDomCustomSerializable interface must also have a public, parameterless constructor.
 Implementing Singleon Objects Serialization

There are cases when an object can have only a limited number of instances. In programming this pattern is called singleton. Singleton serialization is slightly different as the serializer must not create instances of a singleton object. Instead when serializing a singleton the serializer replaces that object with another object (called surrogate). Later when deserialization occurs this surrogate is created and asked to provide a reference to the singleton object.

Let's first take a look at the singleton object which we want to make serializable:

Singleton Object
Copy Code
public class NMySingleton : INDeeplyCloneable, INDomSurrogateSerializable
{
    private NMySingleton()
    {
    }

    // INDeeplyCloneable
    public object DeepClone()
    {
        return this;
    }

    // INDomSurrogateSerializable
    public INDomSurrogateSerializer DomSurrogateSerializer
    {
        get
        {
            return new NMySurrogate(this);
        }
    }

    public static NMySingleton InstanceA = new NMySingleton();
    public static NMySingleton InstanceB = new NMySingleton();
}

This class can have only two instances (its constructor is marked as private) and those instances are accessible from the InstanceA and InstanceB static fields. In order for it to be compatible with DOM serialization we implement the INDomSurrogateSerializable interface. This interface has only one member - the DomSurrogateSerializer property which returns an instance of an object which is used to serialize the state of the singleton. Following is the implementation of the NMySurrogate class:

Surrogate Serializer Object
Copy Code
public class NMySurrogate : INDomSurrogateSerializer
{
    public NMySurrogate()
    {
    }

    public NMySurrogate(NMySingleton instance)
    {
        IsInstanceA = instance == NMySingleton.InstanceA;
    }

    public object GetDomRealObject(NDomDeserializationContext context)
    {
        if (IsInstanceA)
        {
            return NMySingleton.InstanceA;
        }
        else
        {
            return NMySingleton.InstanceB;
        }
    }

    public void Deserialize(NPropertyBag propertyBag)
    {
        IsInstanceA = propertyBag.GetBooleanValue("IsInstanceA");
    }

    public void Serialize(NPropertyBag propertyBag)
    {
        propertyBag.AddValue("IsInstanceA", IsInstanceA);
    }

    bool IsInstanceA;
}

Note that this class implements the INDomSurrogateSerializer interface, which inherits from the INDomCustomSerializable interface. It also has two constructors - one without parameters (which is required in order for the deserializer to create empty instances) and one with parameters which we use in the singleton object implementation.

Now lets take a look at the sequence in which the serializer / deserializer will invoke the methods / properties of those two classes.

Serialization

1. The serializer encounters an object marked as INDomSurrogateSerializable.

2. The serializer calls the DomSurrogateSerializer property and gets a reference to a NMySurrogate object.

3. The serializer calls the Serialize method of the NMySurrogate object.

 

Deserialization

1. The deserializer creates an instance of a custom object - in this case an instance of the NMySurrogate class using the public, parameterless constructor.

2. The deserializer detects that this object is marked as custom serializable (implements the INDomCustomSerializable interface) and calls the Deserialize method.

3. The deserializer detects that this object is marked as a surrogate serializer (implements the INDomSurrogateSerializer interface) and calls the GetDomRealObject method.

4. The deserializer uses the value returned from GetDomRealObject method.