All I wanted to do was simple: add a setting to the application config which held a list of the most recently used file names, complete with a user created template to process the file - so they didn't have to repeat work.
Since it is going in the config file (which is XML), it made sense to use the
XmlSerializer
. Easy:
Dictionary<string, string> recentFiles;
...
xml.Serialize(memoryStream, recentFiles);
Ah.
IDictionary
not supported. MSDN says:
"The XmlSerializer cannot process classes implementing the IDictionary interface. This was partly due to schedule constraints and partly due to the fact that a hashtable does not have a counterpart in the XSD type system. The only solution is to implement a custom hashtable that does not implement the IDictionary interface."
In layman speak: "We ran out of time".
OK, an
IDictionary
is a with-frills
List
of
KeyValuePairs
- I will convert it and rebuild...
XmlSerializer
does not complain about
KeyValuePair
items. Great! Only problem is that it stores everything except the actual and / or value information...
KeyValuePair<string, string> kvp = new KeyValuePair<string, string>("My Key", "MyValue");
using (MemoryStream xml = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(KeyValuePair<string, string>));
serializer.Serialize(xml, kvp);
xml.Seek(0, 0);
byte[] bytes = xml.GetBuffer();
string serialized = System.Text.Encoding.ASCII.GetString(bytes);
}
Generates:
<KeyValuePairOfStringString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
So, a brute force and ignorance approach: create a new class, that can do it:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.IO;
using System.Xml.Serialization;
namespace MyNamespace
{
[Serializable]
public class SerialPair<K, V>
{
#region Properties
public K Key { get; set; }
public V Value { get; set; }
#endregion
#region Constructors
public SerialPair()
{
}
public SerialPair(KeyValuePair<K, V> kvp)
{
Key = kvp.Key;
Value = kvp.Value;
}
public SerialPair(K key, V value)
{
Key = key;
Value = value;
}
static SerialPair()
{
if (!typeof(K).IsSerializable && !(typeof(K) is ISerializable))
{
throw new InvalidOperationException("A serializable Type is required");
}
if (!typeof(V).IsSerializable && !(typeof(V) is ISerializable))
{
throw new InvalidOperationException("A serializable Type is required");
}
}
#endregion
#region Public Methods
public static Dictionary<K, V> DeserializeDictionary(string s)
{
Dictionary<K, V> regeneratedTemplates;
List<SerialPair<K, V>> list;
byte[] combinedBytes = System.Text.Encoding.ASCII.GetBytes(s);
using (MemoryStream sr = new MemoryStream(combinedBytes))
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<SerialPair<K, V>>));
list = (List<SerialPair<K, V>>)deserializer.Deserialize(sr);
regeneratedTemplates = new Dictionary<K, V>();
foreach (SerialPair<K, V> pair in list)
{
regeneratedTemplates.Add(pair.Key, pair.Value);
}
}
return regeneratedTemplates;
}
public static string SerializeDictionary(Dictionary<K, V> dict)
{
string s;
List<SerialPair<K, V>> list = new List<SerialPair<K, V>>(dict.Count);
foreach (KeyValuePair<K, V> kvp in dict.ToArray())
{
SerialPair<K, V> sp = new SerialPair<K, V>(kvp);
list.Add(sp);
}
using (MemoryStream xml = new MemoryStream())
{
XmlSerializer serializer = new XmlSerializer(typeof(List<SerialPair<K, V>>));
serializer.Serialize(xml, list);
xml.Seek(0, 0);
byte[] bytes = xml.GetBuffer();
s = System.Text.Encoding.ASCII.GetString(bytes);
}
return s;
}
#endregion
}
}
There is probably a much, much nicer way to do this (I could have used a collection that
XmlSerializer
does support, for example) but at least this way
I won't forget that it doesn't work and try using it again...
[edit]Error in XML Comment to SerialPair(K, V) constructor caused compilation warning. Fixed - OriginalGriff[/edit]