How to serialize/deserialize to `Dictionary<int, string>` from custom XML not using XElement?
Solution 1
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"));
Solution 2
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?
Solution 3
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.
Solution 4
Based on L.B.'s answer.
Usage:
var serializer = new DictionarySerializer<string, string>();
serializer.Serialize("dictionary.xml", _dictionary);
_dictionary = _titleDictSerializer.Deserialize("dictionary.xml");
Generic class:
public class DictionarySerializer<TKey, TValue>
{
[XmlType(TypeName = "Item")]
public class Item
{
[XmlAttribute("key")]
public TKey Key;
[XmlAttribute("value")]
public TValue Value;
}
private XmlSerializer _serializer = new XmlSerializer(typeof(Item[]), new XmlRootAttribute("Dictionary"));
public Dictionary<TKey, TValue> Deserialize(string filename)
{
using (FileStream stream = new FileStream(filename, FileMode.Open))
using (XmlReader reader = XmlReader.Create(stream))
{
return ((Item[])_serializer.Deserialize(reader)).ToDictionary(p => p.Key, p => p.Value);
}
}
public void Serialize(string filename, Dictionary<TKey, TValue> dictionary)
{
using (var writer = new StreamWriter(filename))
{
_serializer.Serialize(writer, dictionary.Select(p => new Item() { Key = p.Key, Value = p.Value }).ToArray());
}
}
}
Solution 5
I have a struct KeyValuePairSerializable
:
[Serializable]
public struct KeyValuePairSerializable<K, V>
{
public KeyValuePairSerializable(KeyValuePair<K, V> pair)
{
Key = pair.Key;
Value = pair.Value;
}
[XmlAttribute]
public K Key { get; set; }
[XmlText]
public V Value { get; set; }
public override string ToString()
{
return "[" + StringHelper.ToString(Key, "") + ", " + StringHelper.ToString(Value, "") + "]";
}
}
Then, the XML serialization of a Dictionary
property is by:
[XmlIgnore]
public Dictionary<string, string> Parameters { get; set; }
[XmlArray("Parameters")]
[XmlArrayItem("Pair")]
[DebuggerBrowsable(DebuggerBrowsableState.Never)] // not necessary
public KeyValuePairSerializable<string, string>[] ParametersXml
{
get
{
return Parameters?.Select(p => new KeyValuePairSerializable<string, string>(p)).ToArray();
}
set
{
Parameters = value?.ToDictionary(i => i.Key, i => i.Value);
}
}
Just the property must be the array, not the List.
myWallJSON
Updated on July 09, 2022Comments
-
myWallJSON almost 2 years
Having empty
Dictionary<int, string>
how to fill it with keys and values from XML like<items> <item id='int_goes_here' value='string_goes_here'/> </items>
and serialize it back into XML not using XElement?
-
Manuel Schweigert over 11 yearsAs far as I understood, he wants to de-/serialize XML to a dictionary. If Dictionary were serializable, you could use built-in classes like XmlSerializer for the task, but as I said, it isn't.
-
Julian50 about 9 yearsI use your code in a loop. And I have a memory leak. I added XmlSerializer.dispose(). but no changed... is it normal ?
-
Vippy about 8 yearsYou deserialize by calling the "ReadXml" method.
-
Daniel McLaury about 7 yearsYou want the
WriteStartElement("item")
andReadStartElement("item")
and so forth inside theforeach
loop, right? -
matra over 6 yearsSome constructors of XmlSerializer leak memory (temporary generated assembly) - you should cache them See support.microsoft.com/en-us/help/886385/…
-
Giggioz almost 5 years@L.B is it possibile to extend this to serialize a Dictionary<string, List<string>> ? I'm not able to do it, thank you.
-
Alex Zinkevych over 3 yearsCould you please provide an example on how to use this class?