How to serialize/deserialize to `Dictionary<int, string>` from custom XML not using XElement?

C#.NetXmlMono

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

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 - C#

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 - C#

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 - C#

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 - C#

You can use ExtendedXmlSerializer. If you have a class:

public class TestClass
{
    public Dictionary<int, string> Dictionary { get; set; }
}

and create instance of this class:

var obj = new TestClass
{
    Dictionary = new Dictionary<int, string>
    {
        {1, "First"},
        {2, "Second"},
        {3, "Other"},
    }
};

You can serialize this object using ExtendedXmlSerializer:

var serializer = new ConfigurationContainer()
	.UseOptimizedNamespaces() //If you want to have all namespaces in root element
	.Create();

var xml = serializer.Serialize(
	new XmlWriterSettings { Indent = true }, //If you want to formated xml
	obj);

Output xml will look like:

<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:sys="https://extendedxmlserializer.github.io/system" xmlns:exs="https://extendedxmlserializer.github.io/v2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples;assembly=ExtendedXmlSerializer.Samples">
  <Dictionary>
    <sys:Item>
      <Key>1</Key>
      <Value>First</Value>
    </sys:Item>
    <sys:Item>
      <Key>2</Key>
      <Value>Second</Value>
    </sys:Item>
    <sys:Item>
      <Key>3</Key>
      <Value>Other</Value>
    </sys:Item>
  </Dictionary>
</TestClass>

You can install ExtendedXmlSerializer from nuget or run the following command:

Install-Package ExtendedXmlSerializer

Solution 6 - C#

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.

Solution 7 - C#

Write a class A, that contains of an array of class B. Class B should have an id property and a value property. Deserialize the xml to class A. Convert the array in A to the wanted dictionary.

To serialize the dictionary convert it to an instance of class A, and serialize...

Solution 8 - C#

KeyedCollection works like dictionary and is serializable.

First create a class containing key and value:

/// <summary>
/// simple class
/// </summary>
/// <remarks></remarks>
[Serializable()]
public class cCulture
{
    /// <summary>
    /// culture
    /// </summary>
    public string culture;

    /// <summary>
    /// word list
    /// </summary>
    public List<string> list;

    /// <summary>
    /// status
    /// </summary>
    public string status;
}

then create a class of type KeyedCollection, and define a property of your class as key.

/// <summary>
/// keyed collection.
/// </summary>
/// <remarks></remarks>
[Serializable()]
public class cCultures : System.Collections.ObjectModel.KeyedCollection<string, cCulture>
{
    protected override string GetKeyForItem(cCulture item)
    {
        return item.culture;
    }
}

Usefull to serialize such type of datas.

Solution 9 - C#

I use serializable classes for the WCF communication between different modules. Below is an example of serializable class which serves as DataContract as well. My approach is to use the power of LINQ to convert the Dictionary into out-of-the-box serializable List<> of KeyValuePair<>:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Xml.Serialization;

    namespace MyFirm.Common.Data
    {
        [DataContract]
        [Serializable]
        public class SerializableClassX
        {
            // since the Dictionary<> class is not serializable,
            // we convert it to the List<KeyValuePair<>>
            [XmlIgnore]
            public Dictionary<string, int> DictionaryX 
            {
                get
                {
                    return  SerializableList == null ? 
                            null :
                            SerializableList.ToDictionary(item => item.Key, item => item.Value);
                }

                set
                {
                    SerializableList =  value == null ?
                                        null :
                                        value.ToList();
                }
            }

            [DataMember]
            [XmlArray("SerializableList")]
            [XmlArrayItem("Pair")]
            public List<KeyValuePair<string, int>> SerializableList { get; set; }
        }
    }

The usage is straightforward - I assign a dictionary to my data object's dictionary field - DictionaryX. The serialization is supported inside the SerializableClassX by conversion of the assigned dictionary into the serializable List<> of KeyValuePair<>:

    // create my data object
    SerializableClassX SerializableObj = new SerializableClassX(param);

    // this will call the DictionaryX.set and convert the '
    // new Dictionary into SerializableList
    SerializableObj.DictionaryX = new Dictionary<string, int>
    {
        {"Key1", 1},
        {"Key2", 2},
    };

Solution 10 - C#

There is an easy way with Sharpeserializer (open source) :

http://www.sharpserializer.com/

It can directly serialize/de-serialize dictionary.

There is no need to mark your object with any attribute, nor do you have to give the object type in the Serialize method (See here ).

To install via nuget : Install-package sharpserializer

Then it is very simple :

Hello World (from the official website):

// create fake obj
var obj = createFakeObject();

// create instance of sharpSerializer
// with standard constructor it serializes to xml
var serializer = new SharpSerializer();

// serialize
serializer.Serialize(obj, "test.xml");

// deserialize
var obj2 = serializer.Deserialize("test.xml");

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmyWallJSONView Question on Stackoverflow
Solution 1 - C#L.BView Answer on Stackoverflow
Solution 2 - C#Scott ChamberlainView Answer on Stackoverflow
Solution 3 - C#Manuel SchweigertView Answer on Stackoverflow
Solution 4 - C#JbjstamView Answer on Stackoverflow
Solution 5 - C#Wojtpl2View Answer on Stackoverflow
Solution 6 - C#xmedekoView Answer on Stackoverflow
Solution 7 - C#erikHView Answer on Stackoverflow
Solution 8 - C#SmartowebView Answer on Stackoverflow
Solution 9 - C#Michael GView Answer on Stackoverflow
Solution 10 - C#MalickView Answer on Stackoverflow