The greatest problem I have encountered with the .NET framework (through 3.5) is that Dictionary instances are not serializable.  You have to write custom serialization routines and its different if you are doing binary or XML serialization.

An important aspect to XML serialization to me is readability.  If I'm serializing something to XML then I expect it to be readable and I prefer less nodey to more.  I like to use attributes whenever possible.

I originally used some somewhat dated code I found on Matt Berther's blog and while it worked, it gave me the nodey version I didn't care much for:

<dictionary>  
  <item>
    <key>type</key>
    <value>resource</value>
  </item>
  <item>
    <key>version</key>
    <value>1</value>
  </item>
</dictionary>

The value of the nodey version is that when the types represented by TKey and TValue are themselves serializable in a way that can allow them to be represented by their own XML object graphs.  Ideally I wanted a format that could have output like:

<item key="type" value="resource" />

but still fall back to the more advanced nodey version as necessary, and mix and match (TKey could be a string while TValue could be a more complex serializable type, such as a domain class with many properties).

My solution was to establish what classes could be attributable:

public class Dictionary<TKey,TValue> :   IDictionary<TKey, TValue>,  
    ISerializable
    IDictionary  
    IXmlSerializable

...

private readonly static List<Type> _attributableTypes;
static Dictionary()  
{
    _attributableTypes = new List<Type>
    {
        typeof(Boolean),
        typeof(Byte),
        typeof(Char),
        typeof(DateTime),
        typeof(Decimal),
        typeof(Double),
        typeof(Enum),
        typeof(Guid),
        typeof(Int16),
        typeof(Int32),
        typeof(Int64),
        typeof(SByte),
        typeof(Single),
        typeof(String),
        typeof(TimeSpan),
        typeof(UInt16),
        typeof(UInt32),
        typeof(UInt64)
    };
}
private static bool IsAttributable(Type t)  
{
    return _attributableTypes.Contains(t);
}

And the meat of the code, the IXmlSerializable interface implementation:

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

void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)  
{
    // some types can be stored easily as attributes while others     
    // require their own XML rendering
    Func<TKey> readKey;
    Func<TValue> readValue;

    var isAttributable = new { Key = IsAttributable(typeof(TKey)),         
        Value = IsAttributable(typeof(TValue)) };

    // keys
    if (isAttributable.Key)
    {
        readKey = () => (TKey)Convert.ChangeType(            
            reader.GetAttribute("key"), typeof(TKey)
        );
    }
    else
    {
        var keySerializer = new XmlSerializer(typeof(TKey));
        readKey = () =>
        {
            while (reader.Name != "key")
                reader.Read();
            reader.ReadStartElement("key");
            var key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();
            return key;
        };

    }

    // values
    if (isAttributable.Value && isAttributable.Key)
    {
        readValue = () => (TValue)Convert.ChangeType(            
             reader.GetAttribute("value"), typeof(TValue)
        );
    }
    else
    {
        var valueSerializer = new XmlSerializer(typeof(TValue));
        readValue = () =>
        {
            while (reader.Name != "value")
                reader.Read();
            reader.ReadStartElement("value");
            var value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();
            return value;
        };
    }

    var wasEmpty = reader.IsEmptyElement;
    reader.Read();

    if (wasEmpty)
        return;

    while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
    {
       while (reader.NodeType == System.Xml.XmlNodeType.Whitespace)
            reader.Read();
        var key = readKey();
        var value = readValue();
        Add(key, value);

        if (!isAttributable.Key || !isAttributable.Value)
            reader.ReadEndElement();
        else
            reader.Read();
        while (reader.NodeType == System.Xml.XmlNodeType.Whitespace)
            reader.Read();
    }
    reader.ReadEndElement();
}

void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)  
{
    Action<TKey> writeKey;
    Action<TValue> writeValue;

    var isAttributable = new     
    {         
        Key = IsAttributable(typeof(TKey)),         
        Value = IsAttributable(typeof(TValue))     
    };

    if (isAttributable.Key)
    {
        writeKey = v => writer.WriteAttributeString("key",             
            v.ToString()
        );
    }
    else
    {
        var keySerializer = new XmlSerializer(typeof(TKey));
        writeKey = v =>
            {
                writer.WriteStartElement("key");
                keySerializer.Serialize(writer, v);
                writer.WriteEndElement();
            };
    }

    // when keys aren't attributable, neither are values
    if (isAttributable.Value && isAttributable.Key)
    {
        writeValue = v => writer.WriteAttributeString("value",             
            v.ToString()
        );
    }
    else
    {
        var valueSerializer = new XmlSerializer(typeof(TValue));
        writeValue = v =>
        {
            writer.WriteStartElement("value");
            valueSerializer.Serialize(writer, v);
            writer.WriteEndElement();
        };
    }

    foreach (var key in Keys)
    {
        writer.WriteStartElement("item");

        writeKey(key);
        writeValue(this[key]);

        writer.WriteEndElement();
    }
}

Bonus: I also learned that multiline lambdas exist and I snuck in an anonymous type to boot.  I like that I was able to take two separate approaches to XML serialization, distill their interfaces from their inner workings, and then just expose the correct method signature within a method to keep the iterative loop clean of if/else logic (even if I just moved the logic above the loop).  To me this code seems much more readable than if I had kept everything in the loop.

Update: it helps to post working code.  Also, here's a zip file with the implementation and some brief NUnit flavored tests.