По каким-то причинам Microsoft решила сделать класс Dictionary<(TKey, TValue>) не поддерживающим XML-сериализацию.

Т.е. код типа:

1
2
3
4
5
6
7
8
Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1, "aa");
dict.Add(2, "bb");
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Dictionary<int, string>));
using (FileStream fs = new FileStream("test.xml", FileMode.CreateNew))
{
    xmlSerializer.Serialize(fs, dict);
}

выполняться не будет. Будет отображаена информация об ошибке вида:

«Тип System.Collections.Generic.Dictionary`2[.] не поддерживается, т.к. он реализует IDictionary.»

Такой расклад нас не устраивает…

Будем допиливать стандартный класс для поддержки сериализации.

1. Создаем класс наследник от Dictionary, реализующий интерфейс IXmlSerializable:

1
2
[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable

2. И, собственно, реализуем интерфейс:
2.1. Схема нам не нужна:

1
2
3
4
public XmlSchema GetSchema()
{
    return null;
}

2.2. Чтение xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void ReadXml(XmlReader reader)
{
    var keySerializer = new XmlSerializer(typeof(TKey));
    var valueSerializer = new XmlSerializer(typeof(TValue));
    bool wasEmpty = reader.IsEmptyElement;
    reader.Read();
    if (wasEmpty) return;
 
    while (reader.NodeType != XmlNodeType.EndElement)
    {
        reader.ReadStartElement("item");
        reader.ReadStartElement("key");
        var key = (TKey)keySerializer.Deserialize(reader);
        reader.ReadEndElement();
        reader.ReadStartElement("value");
        var value = (TValue)valueSerializer.Deserialize(reader);
        reader.ReadEndElement();
        Add(key, value);
        reader.ReadEndElement();
        reader.MoveToContent();
    }
    reader.ReadEndElement();
}

2.3. Запись xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void WriteXml(XmlWriter writer)
{
    var keySerializer = new XmlSerializer(typeof(TKey));
    var valueSerializer = new XmlSerializer(typeof(TValue));
 
    foreach (TKey key in 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();
    }
}

Все! Класс готов.
Проверяем:

1
2
3
4
5
6
7
8
Dictionary<int, string> dict = new SerializableDictionary<int, string>();
dict.Add(1, "aa");
dict.Add(2, "bb");
XmlSerializer xmlSerializer = new XmlSerializer(typeof(SerializableDictionary<int, string>));
using (FileStream fs = new FileStream("test.xml", FileMode.CreateNew))
{
    xmlSerializer.Serialize(fs, dict);
}

Работает! На выходе получится xml, такого вида:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0"?>
<dictionary>
  <item>
    <key>
      <int>1</int>
    </key>
    <value>
      <string>aa</string>
    </value>
  </item>
  <item>
    <key>
      <int>2</int>
    </key>
    <value>
      <string>bb</string>
    </value>
  </item>
</dictionary>

На всякий случай прикладываю код класса: Скачать


Примечание.
XmlSerializer.Serialize() может принимать не только FileStream, но и любой другой Stream, TextWriter, XmlWriter.
Для совместимости класса с LINQ, необходимо наличие метода ToDictonary.
ToDictionary — это обычный extension метод.
Можно его реализовать например так:

1
2
3
4
5
6
7
8
9
10
public static class ExtensionMethods
{
    public static SerializableDictionary<TKey, TValue> ToSerializableDictionary<TKey, TValue, TSource>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TValue> elementSelector)
    {
        var d = new SerializableDictionary<TKey, TValue>();
        foreach (TSource element in source)
            d.Add(keySelector(element), elementSelector(element));
        return d;
    }
}

и использовать в LINQ:

1
2
3
4
5
var someList = new List<string> {"1", "2", "3", "4"};
var sDict = someList
    .Where(x => x != null)
    .OrderByDescending(x => x)
    .ToSerializableDictionary(key => int.Parse(key), value => value + "qwe");

Информация взята со страницы dev-notes.ru


5th Фев 2017
Теги:
Загрузка Все права защищены © 2017 ИТ-Инженер (Краснодар)
 
把手拿回