Spread Windows Forms 15.0
Spread Windows Forms 15.0 Product Documentation / Developer's Guide / File Operations / Using Serialization / Implementing a Serializer Class
In This Topic
    Implementing a Serializer Class
    In This Topic

    When using the Serializer class, remember these tips:

    The examples below show saving simple things like integers and colors, but saving other types is no different. There are methods in Serializer for saving and loading Color, DateTime, DateTimeFormatInfo, Enum types, Font, Image, Int32 array, NumberFormatInfo, PointF array, String array, and Object. It is best (though not required) to use the most specific method available. For example, it would work to use SerializeObject to save a simple type or a Color value, but it is less efficient and may not result in the same XML.

    In general, there is a reason each of the methods was created for its specific type. DateTimeFormatInfo and NumberFormatInfo must be saved using their respective methods because SerializeObject would default to using binary serialization, which does not work across versions of the framework (so a DateTimeFormatInfo saved using .NET 1.0 does not load using .NET 1.1 or 2.0).

    For simple types (Int32, String, Boolean, etc.) it is best to use ToString() to convert them to a string for saving (passing CultureInfo.InvariantCulture if possible) and then use XmlConvert to convert the strings back into values.

    SaveObject is intended for saving objects which implement ISerializeSupport, or for certain specific types (intrinsic data types and DataSet), or for generic objects which are serializable but do not implement ISerializeSupport (such objects get saved using EncodeObject, which uses a BinaryFormatter to save the object to a MemoryStream and then uses Convert to encode the bytes into a base-64 string).

    Debug versus Release Build

    To get files saved with a debug build to load into a release build that is strong named, you must eliminate or update the assembly attribute in the node where the object is saved. SerializeObject will write an assembly attribute if the assembly in which the object's type is defined is different from the calling assembly and different from the executing assembly (FarPoint.Win.dll).

    The calling assembly is usually FarPoint.Win.Spread.dll but is possibly the user's assembly if they are defining their own objects which implement ISerializeSupport and saving child objects using SerializeObject. The tricky case is, unfortunately, likely the most common one: a user creates a custom object to plug into the Spread's object model (for example, a custom data model) and they want to save and load the Spread using the save and load methods in FpSpread and use different builds of their assembly.

    In that case, the assembly attribute is required to load the correct assembly containing the object's type definition, but the correct attribute for the debug and release builds is different. In that case, the user would need to edit the value in the XML to change the assembly reference (using something like String.Replace).

    The less common cases involve users who are using ISerializeSupport and Serializer to save and load their own files containing objects that they define, perhaps in the same assembly or in a different assembly that they are writing in parallel. If the objects are defined in the same assembly, then the assembly tab will not be written out, and if it is a different assembly, the developer can call the overloads for SerializeObject and CreateObjectInstanceAndDeserialize which take the callingAssembly argument and pass the assembly containing the object (even if that is not really the assembly calling into Serializer). That will prevent the assembly attribute from being written out, but it is important that this be done on both ends (the call to save and load) so that the type reference can be resolved and an instance of the object created.

    Here are some examples of how you might implement ISerializeSupport.

    Using Code

    This example uses the base class.

    Example

    The following code provides the implementation of ISerializeSupport in FarPoint.Win.LineBorder:

    C#
    Copy Code
    // A constructor with no arguments is required for ISerializeSupport.
    // It does not need to be public though.
        internal LineBorder()
        {
            color = SystemColors.WindowFrame;
            thickness = 1;
            left = top = right = bottom = true;
            inset = new Inset(1);
        }
      /// <summary>
    /// Saves the object to XML.
    /// </summary>
    /// <param name="w">XmlTextWriter object to which to save the object</param>
    /// <returns>true if successful; false otherwise</returns>
        public virtual bool Serialize(XmlTextWriter w)
        {
            w.WriteStartElement("Inset");
            w.WriteElementString("Bottom",inset.Bottom.ToString());
            w.WriteElementString("Left", inset.Left.ToString());
            w.WriteElementString("Right", inset.Right.ToString());
            w.WriteElementString("Top", inset.Top.ToString());
            w.WriteEndElement();
            Serializer.SerializeColor(color, "Color", w);
            w.WriteStartElement("Sides");
            w.WriteElementString("Bottom", bottom.ToString());
            w.WriteElementString("Left", left.ToString());
            w.WriteElementString("Right", right.ToString());
            w.WriteElementString("Top", top.ToString());
            w.WriteEndElement();
            w.WriteElementString("Thickness", thickness.ToString(CultureInfo.InvariantCulture));
            return true;
        }
      
    /// <summary>
    /// Loads the object from XML.
    /// </summary>
    /// <param name="r">XmlNodeReader from which to load the object</param>
    /// <returns>true if successful; false otherwise</returns>
        public virtual bool Deserialize(XmlNodeReader r)
        {
            bool inInset = false;
            bool inBottom = false;
            bool inLeft = false;
            bool inRight = false;
            bool inTop = false;
            bool inColor = false;
            bool inSides = false;
            bool inThickness = false;
            int bottom = inset.Bottom;
            int left = inset.Left;
            int right = inset.Right;
              if( r.IsEmptyElement )
                return true;
            while( r.Read() )
            {
                switch( r.NodeType )
                {
                case XmlNodeType.Element:
                if( r.Name.Equals("Inset") )
                   inInset = true;
                else if( r.Name.Equals("Color") )
                   inColor = true;
                else if( r.Name.Equals("Sides") )
                   inSides = true;
                else if( inInset || inSides )
                {
                if( r.Name.Equals("Bottom") )
                   inBottom = true;
                else if( r.Name.Equals("Left") )
                   inLeft = true;
                else if( r.Name.Equals("Right") )
                   inRight = true;
                else if( r.Name.Equals("Top") )
                   inTop = true;
                }
                else if( r.Name.Equals("Thickness") )
                   inThickness = true;
                break;
             case XmlNodeType.Text:
                if( inInset )
    {
    if( inBottom )
    bottom = XmlConvert.ToInt32(r.Value);
    else if( inLeft )
    left = XmlConvert.ToInt32(r.Value);
    else if( inRight )
    right = XmlConvert.ToInt32(r.Value);
    else if( inTop )
    inset = new Inset(left, XmlConvert.ToInt32(r.Value), right, bottom);
    }
    else if( inSides )
    {
    if( inBottom )
    this.bottom = XmlConvert.ToBoolean(r.Value.ToLower(CultureInfo.InvariantCulture));
    else if( inLeft )
    this.left = XmlConvert.ToBoolean(r.Value.ToLower(CultureInfo.InvariantCulture));
    else if( inRight )
    this.right = XmlConvert.ToBoolean(r.Value.ToLower(CultureInfo.InvariantCulture));
    else if( inTop )
    this.top = XmlConvert.ToBoolean(r.Value.ToLower(CultureInfo.InvariantCulture));
    }
    else if( inColor )
    color = Serializer.DeserializeColorValue(r.Value);
    else if( inThickness )
    thickness = XmlConvert.ToInt32(r.Value);
    break;
    case XmlNodeType.EndElement:
    if( inInset && r.Name.Equals("Inset") )
    inInset = false;
    else if( inColor && r.Name.Equals("Color") )
    inColor = false;
    else if( inSides && r.Name.Equals("Sides") )
    inSides = false;
    else if( inInset || inSides )
    {
    if( inBottom && r.Name.Equals("Bottom") )
    inBottom = false;
    else if( inLeft && r.Name.Equals("Left") )
    inLeft = false;
    else if( inRight && r.Name.Equals("Right") )
    inRight = false;
    else if( inTop && r.Name.Equals("Top") )
    inTop = false;
    }
    else if( inThickness && r.Name.Equals("Thickness") )
    // return here so that subclasses can deserialize
    return true;
    break;
    }
    }
    return true;
    }
    

    Example

    This is an example of what a derived class might do in overrides for those methods:

    C#
    Copy Code
    // A constructor with no arguments is required for ISerializeSupport.
    // It does not need to be public though.
    protected GradientLineBorder() : LineBorder(SystemColors.WindowFrame, 1, true, true, true, true)
    {
    startColor = Color.Blue;
    endColor = Color.Green;
    }
    /// <summary>
    /// Saves the object to XML.
    /// </summary>
    /// <param name="w">XmlTextWriter object to which to save the object</param>
    /// <returns>true if successful; false otherwise</returns>
    public override bool Serialize(XmlTextWriter w)
    {
    if( !base.Serialize(w) )
    return false;
    Serializer.SerializeColor(startColor, "StartColor", w);
    Serailizer.SerializeColor(endColor, "EndColor", w);
    return true;
    }
    /// <summary>
    /// Loads the object from XML.
    /// </summary>
    /// <param name="r">XmlNodeReader from which to load the object</param>
    /// <returns>true if successful; false otherwise</returns>
    public override bool Deserialize(XmlNodeReader r)
    {
    bool inStartColor = false;
    bool inEndColor = false;
    
    if( r.IsEmptyElement )
    return true;
    if( !base.Deserialize(r) )
    return false;
    while( r.Read() )
    {
    switch( r.NodeType )
    {
    case XmlNodeType.Element:
    if( r.Name.Equals("StartColor") )
    inStartColor = true;
    else if( r.Name.Equals("EndColor") )
    inEndColor = true;
    case XmlNodeType.Text:
    if( inStartColor )
    startColor = Serializer.DeserializeColorValue(r.Value);
    else if( inEndColor )
    endColor = Serializer.DeserializeColorValue(r.Value);
    break;
    case XmlNodeType.EndElement:
    if( inStartColor && r.Name.Equals("StartColor") )
    inStartColor = false;
    else if( inEndColor && r.Name.Equals("EndColor") )
    // return here so that subclasses can deserialize
    return true;
    break;
    }
    }
    return true;
    }
    

    Example

    Another (somewhat easier) way to implement it would be this way:

    C#
    Copy Code
    /// <summary>
    /// Saves the object to XML.
    /// </summary>
    /// <param name="w">XmlTextWriter object to which to save the object</param>
    /// <returns>true if successful; false otherwise</returns>
    public override bool Serialize(XmlTextWriter w)
    { w.WriteAttributeString("startColor", Serializer.SerializeColorValue(startColor));
    w.WriteAttributeString("endColor", Serializer.SerializeColorValue(endColor));
    return base.Serialize(w);
    }
    /// <summary>
    /// Loads the object from XML.
    /// </summary>
    /// <param name="r">XmlNodeReader from which to load the object</param>
    /// <returns>true if successful; false otherwise</returns>
    public override bool Deserialize(XmlNodeReader r)
    {
    
    if( r.MoveToAttribute("startColor") )
    startColor = Serializer.DeserializeColorValue(r.Value);
    if( r.MoveToAttribute("endColor") )
    endColor = Serializer.DeserializeColorValue(r.Value);
    return base.Deserialize(r); }
    

    See Also