How to serialize/deserialize to `Dictionary<int, string>` from custom XML not using XElement?
With the help of a temporary item
class
public class item
{
[XmlAttribute]
public int id;
[XmlAttribute]
public string value;
}
Sample Dictionary:
Dictionary<int, string> dict = new Dictionary<int, string>()
{
{1,"one"}, {2,"two"}
};
.
XmlSerializer serializer = new XmlSerializer(typeof(item[]),
new XmlRootAttribute() { ElementName = "items" });
Serialization
serializer.Serialize(stream,
dict.Select(kv=>new item(){id = kv.Key,value=kv.Value}).ToArray() );
Deserialization
var orgDict = ((item[])serializer.Deserialize(stream))
.ToDictionary(i => i.id, i => i.value);
------------------------------------------------------------------------------
Here is how it can be done using XElement, if you change your mind.
Serialization
XElement xElem = new XElement(
"items",
dict.Select(x => new XElement("item",new XAttribute("id", x.Key),new XAttribute("value", x.Value)))
);
var xml = xElem.ToString(); //xElem.Save(...);
Deserialization
XElement xElem2 = XElement.Parse(xml); //XElement.Load(...)
var newDict = xElem2.Descendants("item")
.ToDictionary(x => (int)x.Attribute("id"), x => (string)x.Attribute("value"));
Paul Welter's ASP.NET blog has a dictionary that is serializeable. But it does not use attributes. I will explain why below the code.
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue>
: Dictionary<TKey, TValue>, IXmlSerializable
{
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
bool wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
return;
while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
{
reader.ReadStartElement("item");
reader.ReadStartElement("key");
TKey key = (TKey)keySerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadStartElement("value");
TValue value = (TValue)valueSerializer.Deserialize(reader);
reader.ReadEndElement();
this.Add(key, value);
reader.ReadEndElement();
reader.MoveToContent();
}
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
foreach (TKey key in this.Keys)
{
writer.WriteStartElement("item");
writer.WriteStartElement("key");
keySerializer.Serialize(writer, key);
writer.WriteEndElement();
writer.WriteStartElement("value");
TValue value = this[key];
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
writer.WriteEndElement();
}
}
#endregion
}
First, there is one gotcha with this code. Say you read a dictionary from another source that has this:
<dictionary>
<item>
<key>
<string>key1</string>
</key>
<value>
<string>value1</string>
</value>
</item>
<item>
<key>
<string>key1</string>
</key>
<value>
<string>value2</string>
</value>
</item>
</dictionary>
This will throw a exception on de-seariazation because you can only have one key for a dictionary.
The reason you MUST use a XElement in a seriazed dictionary is dictionary is not defined as Dictionary<String,String>
, a dictionary is Dictionary<TKey,TValue>
.
To see the problem, ask your self: Lets say we have a TValue
that serializes in to something that uses Elements it describes itself as XML (lets say a dictionary of dictionaries Dictionary<int,Dictionary<int,string>>
(not that uncommon of a pattern, it's a lookup table)), how would your Attribute only version represent a dictionary entirely inside a attribute?
Dictionaries are not Serializable in C# by default, I don't know why, but it seems to have been a design choice.
Right now, I'd recommend using Json.NET to convert it to JSON and from there into a dictionary (and vice versa). Unless you really need the XML, I'd recommend using JSON completely.