Click here to Skip to main content
Email Password   helpLost your password?

Sample Image - xmlsettings.gif

Introduction

When I first teamed up with Christian the screensaver didn't have a very elegant way of persisting its settings. In the constructor for the screensaver form settings were loaded from the registry, and the same code was copied and pasted into the constructor for the Options dialog.

After my failure of trying to speed up the drawing process (curse you Microsoft for making CachedBitmap an internal class), the first task I brought myself to was to try to clean up the settings code. What resulted is the Settings class; which can be used for storing settings for the user or application.

This class differs slightly from the one in the screensaver because it has been refactored a bit but the premise behind it is the same.

Class make-up

Unless specified, members are assumed to be public instance.

How to use the class

  1. Alter the class to include your own properties
    1. Add a new public get/set property to the class.
    2. In the set part, have it set the value in the Hashtable using the property name as a key.
    3. In the get part, have it return the value stored in the Hashtable using the property name as the key (you will probably need to cast the value from the Hashtable)
  2. Test to ensure there is no problem with the underlying types; if there are no problems then the rest of the article is just information for you to digest. If there were problems then read on to discover why there were problems and how you can work around them.

The XmlSerializer class

The XmlSerializer class does most of the work needed for saving the data in a class to a file. Unlike the other serializers the XmlSerializer requires the classes it serializes to have a public default constructor (a public constructor that takes no parameters) and it only serializes public properties and fields.

Because of this it isn't well suited for classes with lots of internal data, unless those can be completely rebuilt via public properties/field. Because of this limitation, certain types won't be serialized by it, a couple examples are System.Drawing.Color and System.Drawing.Font. I don't have a list of all the classes that fail so you'll just have to use trial and error to find them, if serialization of the class isn't supported an exception will be thrown when you try to create the XmlSerializer or the resulting serialization will be an empty tag. I describe a couple techniques to work around this, and I provided fixex for both Color and Font classes so they work, with little effort.

Serializing and deserializing a class with the XmlSerializer is really easy, because it is I'm not going to spend much time on it; but instead I'll spend most of my time describing how to work around its limitations. Simply create a new instance of the XmlSerializer class passing in the Type that corresponds to your class.

XmlSerializer xs = new XmlSerializer(typeof(Settings));

To serialize a class, call Serialize() and pass in a Stream and an instance of the class you wish to serialize. To deserialize, call Deserialize() and pass in the Stream to read from, and cast the return value back to your class type.

Working around the limitations

Custom parsing

System.Drawing.Color is the first limitation I worked around. I did this by first deciding how I was going to store the data in the XML file. I chose a string delimited by colons (:). This string is made up of two parts, the first part tells what type of data follows, either a color name or ARGB values. The second part is either a name or the values split up by colons.

First I created an enumeration that would signify each type.

public enum ColorFormat
{
    NamedColor,
    ARGBColor
}

Then with Color in hand I can reference the IsNamedColor property to decide which of the two formats to return. I do that with this bit of code.

public string SerializeColor(Color color)
{
    if( color.IsNamedColor )
        return string.Format("{0}:{1}", 
            ColorFormat.NamedColor, color.Name);
    else
        return string.Format("{0}:{1}:{2}:{3}:{4}", 
            ColorFormat.ARGBColor, 
            color.A, color.R, color.G, color.B);
}

If you inspect the return value you'll see that what is written for the ColorFormat specifier is the name of the enum value. This causes a slight problem, but is quickly remedied by the Enum class having a Parse method which will convert the name back to a value.

Speaking of reading the values back, here is how I did it. First I take the string in and split it up using colon as the separation character. This results in a string array with 2 or 5 elements in it. I then take the first element and run it through Enum's Parse() method to convert it back to a value suitable for the enum. Then I check that value to determine which of the two formats it is.

public Color DeserializeColor(string color)
{
    byte a, r, g, b;

    string [] pieces = color.Split(new char[] {':'});
		
    ColorFormat colorType = (ColorFormat) 
        Enum.Parse(typeof(ColorFormat), pieces[0], true);

    switch(colorType)
    {
        case ColorFormat.NamedColor:
            return Color.FromName(pieces[1]);

        case ColorFormat.ARGBColor:
            a = byte.Parse(pieces[1]);
            r = byte.Parse(pieces[2]);
            g = byte.Parse(pieces[3]);
            b = byte.Parse(pieces[4]);
			
            return Color.FromArgb(a, r, g, b);
    }
    return Color.Empty;
}

With that code it is now possible serialize a Color object as a string; which the XmlSerializer will handle. How does one go about doing that, and make the resulting XML look as though it handled it natively? By using two attributes you can change the name an element will have in the XML document and tell it ignore other elements as well.

The attribute XmlIgnoreAttribute when applied to a property or field tells the XmlSerializer to ignore that field/property when serializing the class. The attribute XmlElementAttribute does various functions, but the one we're interested in is renaming a serialized element to something else. For our purposes we will apply the XmlIgnore attribute to the original property which can't be serialized; then we'll apply the XmlAttribute to the XmlSerializer friendly property to rename it something more suitable.

A small example

[XmlIgnore()] 
public Color ColorType
{
    get
    {
        return (Color) settings["color"];
    }
    set
    {
        settings["color"] = value;
    }
}

[XmlElement("ColorType")]
public string XmlColorType
{
    get
    {
        return Settings.SerializeColor(ColorType);
    }
    set
    {
        ColorType = Settings.DeserializeColor(value);
    }
}

Here you see that I have a public property named ColorType that will return the Color stored in the settings object. There is also a string property that is used by the XmlSerializer to store the underlying value.

ColorType is the property that the user of the class is to use for setting/retrieving properties. XmlColorType is used by the XmlSerializer to get/set the underlying value and should not be used by the programmer.

Wrapper class/struct

Another way to work around the limitation is to create a class that exposes only the properties needed to recreate the object, and use that to serialize.

This is what I did for the Font class; exposing the FontFamily, Size, FontStyle, and the GraphicsUnit associated with a Font instance. In keeping with the pattern I used for Color; I supply a property that uses this class, and I have two protected methods which handle moving back and forth between the two types.

XmlFont struct

public struct XmlFont
{
    public string FontFamily;
    public GraphicsUnit GraphicsUnit;
    public float Size;
    public FontStyle Style;

    public XmlFont(Font f)
    {
        FontFamily = f.FontFamily.Name;
        GraphicsUnit = f.Unit;
        Size = f.Size;
        Style = f.Style;
    }

    public Font ToFont()
    {
        return new Font(FontFamily, Size, Style, 
            GraphicsUnit);
    }
}

This is an extremely simple struct. Its entire purpose is to be a lightweight container for the values that will be persisted to the file I don't bother defining properties, and I made it a struct instead of a class so it *is* lightweight. Depending on your needs you could use a heavier implementation; but for this case this works great.

This works because the XmlSerializer will attempt to serialize every property and field, if the property or field is a simple datatype it puts it inline, for structs and classes it serializes its public properties/fields.

Conclusion

I've often wondered why it is that MS didn't let the XmlSerializer work like the Formatters, serializing both public and private data. After toying around with the Settings class a bit I think I discovered why. There isn't a need! The XmlSerializer was made to persist data in a user-readable way, but still be read easily by a program. Publishing private data would be considered a Bad Thing� and thus would make life difficult for those trying to keep private data private. Though you could go through and add NonSerializable attributes to all your private data, you would be SOL when it came to remoting.

Considering that the workaround for the deficiencies isn't all that difficult to get working I'm coming to agree with Microsoft's decision.

As always bug reports should be posted below, comments or questions can be posted or e-mailed to me.

If you want the XP theme I used in the screenshot, check out the WaterColor Visual Style from ThemeXP.org.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralAnother (simpler?) alternative
Rynus
0:20 6 May '09  
This is my usual way of serializing colors. Only adds an extra property, understandable and your XML stays readable.


[XmlIgnore]
public Color SomeColor { get; set; }

[XmlElement("SomeColor")]
public int SomeColorSerializable
{
get { return SomeColor.ToArgb(); }
set { SomeColor = Color.FromArgb(value); }
}

GeneralSort of overkill - There is a simpler way.
David Rock
13:53 8 Apr '09  
System.Drawing.Color is not serializable. However, it contains methods that make serialization very easy. Specifically ToArgb and FromArgb. These two methods can be used to convert a System.Drawing.Color to a long INT value (4 bytes, Alpha, Red, Green, Blue respectively). So, simple serialization can be achieved via the following


using System.Xml;
using System.Xml.Serialization;

[Serializable]
public class SimpleColorExample
{
private System.Drawing.Color color;

public SimpleColorExample()
{
this.color = System.Drawing.Color.FromArgb((int) 0);
}

public SimpleColorExample(System.Drawing.Color Color)
{
this.color = Color;
}



[XmlIgnore]
public Color
{
get { return this.color; }
set { this.color = value; }
}

public int ColorARGB
{
get { return this.color.ToArgb(); }
set { this.color = System.Drawing.Color.FromArgb(value); }
}
}


This simple class works very easily. The end user can access the Color property as would normally be done so. Note the XmlIgnore tag. This
tells serialization to ignore that property and thus, when serialization occurs, that property (Color) is ignored. However, the ColorARGB property is included in the serialization but because it is a simple wrapper routine dealing with a simple data type (int), it is serializable. What you would see in the file would be a long int value that represents the 4 bytes that define the color. It is not elegant for display of XML in a text editor but so what, we aren't suppose to be editing XML directly anyway. It still parses back as a color object per the XML specification so I see no reason to waste any more effort on it.
GeneralRe: Sort of overkill - There is a simpler way.
denis.gz
7:08 17 Jul '09  
Better yet, use TypeConverter:

private Color m_color;
[XmlElement("TheColor")]
public string XmlTheColor
{
get { return TypeDescriptor.GetConverter(typeof(Color)).ConvertToString(m_color); }
set { m_color = (Color) TypeDescriptor.GetConverter(typeof(Color)).ConvertFrom(value); }
}

The ColorConverter object, which is returned by the GetConverter(), understands both known color names and R;G;B style strings for custom colors, so it's best to use for human readable configs. For known colors, you'll get meaningful strings (even ButtonShadow and such are acceptable),and for custom colors, you'll get exact representation.
GeneralFack you
Gatman
3:26 18 Apr '08  
You stupid fat faggot you don't even understand the basic of serialization
GeneralRe: Fack you
James T. Johnson
3:49 18 Apr '08  
Gatman wrote:
You stupid fat faggot you don't even understand the basic [sic] of serialization


I don't know what my sexual orientation has to do with being able to share my knowledge and techniques with you. At the very least, if you're going to call someone stupid, you should at least make sure all the words in your post are spelled correctly.

Cheers,

James

GeneralRe: Fack you
Luca Crisi, MCP
22:37 16 Jun '08  
You are an idiot.

1st: Be thankful to someone who shares his/her knowledge.
2nd: Don't be offensive.
3rd: We're not here to talk about sexuality - That's a private variable.

Luca Crisi, MCP

GeneralNini
Mattman206
11:02 20 Jul '06  
Great article! Good overview of XML Serialization.

Also, if you've never heard of NIni, it's really worth checking out. It's a library for reading in application configurations, from a varietry of different formats like XML, Windows INI, registry, etc.



-Matt


Bart: Look at me, I'm a grad student. I'm 30 years old and I made $600 last year.
Marge: Bart, don't make fun of grad students. They've just made a terrible life choice.
GeneralFile Object
VirtuaGuy
0:11 17 Feb '05  
How would you implement a file object? Settings will save its relative or absolute path information?

Thanks a lot
Ferhat

Thanks
GeneralDesign wrapper class for SqlCommand object
dlakme
1:55 22 Jul '04  
Hi,

I want to serialize SqlCommand but am not able to because it contains a member Site which implement interface ISite. So someone suggest me to design a wrapper class. His code is very simple, but I feel I'm missing something.
I'm very new to XML, in fact this is my first time doing this. So I'm very appreciated if anybody can help me.

Here is the advised code:


[Serializable]
class SqlCommandSerializer
{
private string m_sCommand;

public SqlCommandSerializer() { }

public string Command
{
get
{
return m_sCommand;
}

set
{
m_sCommand = value;
}
}

public SqlCommand CreateCommand() // whatever parameters you want
{
// do your creation of the command in here
}
}

// in code
// presuming you're using a formatter called 'formatter'

SqlCommandSerializer serializer = formatter.ReadObject() as SqlCommandSerializer;

if (serializer != null)
{
SqlCommand command = serializer.CreateCommand();
}


The thing is if I only serialize SQlCommandSerializer, then how can I capture the information from SqlCommand command object

Thank you
GeneralHelp needed
Zeeba
18:49 4 Feb '04  
Using Basic Xml Serialization, is it possible to serialize and deserialize static fields ? If yes, how? Please reply asap.
GeneralRe: Help needed
mahesh.cr
21:34 4 Feb '04  
No I would not think so. The XmlSerializer works on class instances and not the classes themselves. And statics are considered part of the class definition and not the object instance.


GeneralDynamic Control over which Properties to display ?
jezbo
2:29 2 Jan '04  
I see that the Browsable attribute for each property defines whether it is displayed in the property grid or not, but this seems to be a static constant (true or false) interpreted at compile-time.

Is there any way to configure which attributes to show or hide dynamically at run-time? I want to use the property grid but under certain scenarios hide specific properties or make them read-only, and I dont want to create separate classes for each possible combination.

JwB
GeneralRe: Dynamic Control over which Properties to display ?
flaming_red_dingo
6:42 17 Mar '05  
Yes - do a search for System.ComponentModel.TypeDescriptor and System.ComponentModel.PropertyDescriptor on google - or even on this site. i seem to remember perusing an article titled something like "Bending the propertygrid to your will" on codeproject a year or so ago. That should serve to enlighten you on the rather gritty details . The component-model can be a bit wierd at first, but it does tend to grow on you. After banging on it for awhile, and running down a few (or few dozen) blind alleys, I finally grokked it and and discovered its elegant side. I hope you have the same or better luck with it. Look for the article I mentioned. The article is pretty good, if memory serves, and as far as the associated code goes - as usual it speaks volumes...

Regards
dj


black holes are where god divided by zero
GeneralAnother alternative for Color
Andy Weston
18:59 31 Aug '03  
Good article. Here's another way that I came up with where I maintain two representations of the field, the second of which can be serialized.
    [Serializable]
public class ColorData
{
protected Color _color = SystemColors.Control;
[XmlIgnore]
public Color Color
{
set { _color = value; _colorInt = value.ToArgb(); }
get { return _color; }
}
protected int _colorInt;
public int ColorInt
{
set { _colorInt = value; _color = Color.FromArgb(value); }
get { return _colorInt; }
}

public ColorData()
{
}
}


--Andy Weston
GeneralRe: Another alternative for Color
maciejr
11:58 22 Aug '05  
It is even nicer if you format the color as an 8-digit hex value. Then you can see the a, r, g, b components in the XML file! Smile
Here is an example (in VB.Net):

<XmlIgnore()> Public border_color As System.Drawing.Color
Public Property BorderColor() As String
Get
Return String.Format("0x{0:x8}", border_color.ToArgb())
End Get
Set(ByVal Value As String)
border_color = Drawing.Color.FromArgb(Convert.ToInt32(Value, 16))
End Set
End Property

GeneralSmall improvement
ITGFanatic
10:26 7 Nov '06  
You don't need to keep _colorInt around as a protected member. Just set _color for ColorInt's set, and return _color.ToArgb() for ColorInt's get.

I'm not sure how to combine this with the sibling post's suggestion about outputting in hex format to the XML file, but that would be icing on the cake.

    [Serializable]
public class ColorData
{
protected Color _color = SystemColors.Control;
[XmlIgnore()]
public Color Color
{
set { _color = value; }
get { return _color; }
}

// protected int _colorInt;
public int ColorInt
{
// set { _colorInt = value; _color = Color.FromArgb(value); }
// get { return _colorInt; }
set { _color = Color.FromArgb(value); }
get { return _color.ToArgb(); }
}

public ColorData()
{
}
}


Cheers,
AJ
GeneralAn alternative method for serializing System.Drawing.Color
Furty
16:54 17 Jul '03  
Unfortunately I discovered this article after I had developed my own workaround for serializing Color's to Xml. However, the way I did it is slightly different - I created a new class with a Color member, and implemented the IXmlSerializable interface so the Xml Serializers know how to read and write it. Anyway, here's the code for anyone interested:


public class XmlColor : System.Xml.Serialization.IXmlSerializable
{
#region Private Properties

private Color color;

#endregion

#region Public Properties

public Color Color
{
get { return color; }
set { color = value; }
}

#endregion

#region Constructor

public XmlColor() : this(Color.Black)
{}

public XmlColor(Color color)
{
this.color = color;
}

#endregion

#region IXmlSerializable Members

System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema()
{
return null;
}

void ValidationCallback(object sender, System.Xml.Schema.ValidationEventArgs args)
{
}

void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteString( this.color.ToArgb().ToString() );
}

void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
{
reader.ReadStartElement();
try
{
this.color = Color.FromArgb( Convert.ToInt32( reader.ReadString() ) );
}
catch
{
this.color = Color.Black;
}
reader.ReadEndElement();
}

#endregion // IXmlSerializable Members
}

GeneralRe: An alternative method for serializing System.Drawing.Color
Mattman206
11:00 20 Jul '06  
Here is (yet) another (good) way to serialize System.Drawing.Color.

Worked for me. Smile



Bart: Look at me, I'm a grad student. I'm 30 years old and I made $600 last year.
Marge: Bart, don't make fun of grad students. They've just made a terrible life choice.
GeneralRe: An alternative method for serializing System.Drawing.Color
benjol
23:39 19 Nov '09  
This is actually better, because it means you can serialise collections of colors, which is not the same as just being able to serialise a Color property.

Here is a mild improvement, which allows client code to not care about whether it is using XmlColor or Color (untested snippet):


private Color _color;

public XmlColor()
: this(Color.Black)
{ }

public XmlColor(Color color)
{
this._color = color;
}

public static implicit operator Color(XmlColor color)
{
return color._color;
}
public static implicit operator XmlColor(Color color)
{
return new XmlColor(color);
}

GeneralTypeConverter
Matej K.
8:24 5 Mar '03  
To write even fewer lines of code, you could leverage TypeConverter's ConvertTo/ConvertFrom functionality to convert Color and Font to strings. Wink

to string:
Color c = Color.FromArgb(10, 20, 30, 40);
TypeConverter cc = TypeDescriptor.GetConverter(c);
string s = cc.ConvertTo(c, typeof(string)) as string;

The resulting string would be "10; 20; 30; 40" or "Red" if you put Color.Red.

from string:
Color x = (Color) cc.ConvertFrom(s);

GeneralLove that word "refactored"
Marc Clifton
7:58 7 Oct '02  
Why can't we use old fashioned words like "reworked", "updated", "enhanced", "modified"?

Great article, BTW!

Marc
GeneralRe: Love that word "refactored"
flaming_red_dingo
6:19 17 Mar '05  
wow. D'Oh! If learning a new word such as "refactor" causes you so much pain as to take the time to whine and complain about it in a public posting I'd hate to see what kind of response you'd have to the constant demands that actual software development would place on your learning ability-

You may want to switch persuits, and look into doing something easy - get a masters degree in business administration for instance, From what I hear, you can free your entire college itenerary of all of those burdensome courses that require things such as reasoning, learning, or paying attention, for that matter. And I here you can get pretty high-profile, low-accountability careers with one of those things too... It worked for Bush jr. and he can barely read or write.

Oh, and to answer your question, refactoring is a formal process with established patterns. On the otherhand modifying or enhancing is something you can do while drunk.

If your post was an attempt at humor - you should probably know - you're not funny - at least not in the way you intended.

Good luck with that learning aversion man... that seems rough.Sniff
D'Oh!


D. John
[ flaming red dingo software LLC ]
GeneralRe: Love that word "refactored"
Marc Clifton
6:51 17 Mar '05  
wow is right. I make a one line post, and you feel necessary to insult, complain, criticize, and condemn in a short essay.

Laugh

Marc

MyXaml
Advanced Unit Testing
YAPO


GeneralRe: Love that word "refactored"
flaming_red_dingo
17:14 31 Mar '05  
You expect something different when you whine about learning a new word?

I have a shampoo recommendation for you:
http://maddox.xmission.com/crybaby.gif

It may help.

Regards,
dj


GeneralRe: Love that word "refactored"
Keith Farmer
12:01 4 May '05  
Such professionalism. I'll be sure to recommend your services for the contracting work my department's needing to fill.


Last Updated 21 May 2002 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010