Имея пустой Dictionary<int, string>
, как заполнить его ключами и значениями из XML, например
<items>
<item id='int_goes_here' value='string_goes_here'/>
</items>
и сериализовать его обратно в XML, не используя XElement?
Имея пустой Dictionary<int, string>
, как заполнить его ключами и значениями из XML, например
<items>
<item id='int_goes_here' value='string_goes_here'/>
</items>
и сериализовать его обратно в XML, не используя XElement?
С помощью временного item
класса
public class item
{
[XmlAttribute]
public int id;
[XmlAttribute]
public string value;
}
Пример словаря:
Dictionary<int, string> dict = new Dictionary<int, string>()
{
{1,"one"}, {2,"two"}
};
.
XmlSerializer serializer = new XmlSerializer(typeof(item[]),
new XmlRootAttribute() { ElementName = "items" });
Сериализация
serializer.Serialize(stream,
dict.Select(kv=>new item(){id = kv.Key,value=kv.Value}).ToArray() );
Десериализация
var orgDict = ((item[])serializer.Deserialize(stream))
.ToDictionary(i => i.id, i => i.value);
Вот как это можно сделать с помощью XElement, если вы передумаете.
Сериализация
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(...);
Десериализация
XElement xElem2 = XElement.Parse(xml); //XElement.Load(...)
var newDict = xElem2.Descendants("item")
.ToDictionary(x => (int)x.Attribute("id"), x => (string)x.Attribute("value"));
Пол Уэлтер У блога ASP.NET есть словарь, который можно сериализовать. Но он не использует атрибуты. Я объясню, почему ниже кода.
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
}
Во-первых, есть один ключ с этим кодом. Скажем, вы читали словарь из другого источника, который имеет это:
<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>
Это вызовет исключение из-за того, что вы можете использовать только один ключ для словаря.
Причина, по которой вы ДОЛЖНЫ использовать XElement в словарном словаре, не является словарем Dictionary<String,String>
, словарь - это Dictionary<TKey,TValue>
.
Чтобы увидеть проблему, спросите себя: Давайте скажем, что у нас есть TValue
который сериализуется в нечто, что использует Elements, он описывает себя как XML (скажем, словарь словарей Dictionary<int,Dictionary<int,string>>
(не так уж редко шаблона, это таблица поиска)), как ваша версия только для атрибутов будет представлять словарь полностью внутри атрибута?
Словари не являются Serializable в С# по умолчанию, я не знаю почему, но, похоже, это был выбор дизайна.
Прямо сейчас, я рекомендую использовать Json.NET для преобразования его в JSON и оттуда в словарь (и наоборот). Если вам действительно не нужен XML, я бы рекомендовал использовать JSON полностью.
На основании L.B. Ответ.
Использование:
var serializer = new DictionarySerializer<string, string>();
serializer.Serialize("dictionary.xml", _dictionary);
_dictionary = _titleDictSerializer.Deserialize("dictionary.xml");
Общий класс:
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());
}
}
}
У меня есть структура 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, "") + "]";
}
}
Затем сериализация XML в свойстве Dictionary
имеет значение:
[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);
}
}
Просто свойство должно быть массивом, а не списком.
Напишите класс A, содержащий массив класса B. Класс B должен иметь свойство id и свойство value. Deserialize xml в класс A. Преобразуйте массив в в требуемый словарь.
Чтобы сериализовать словарь, преобразуйте его в экземпляр класса A и сериализуйте...
Вы можете использовать ExtendedXmlSerializer. Если у вас есть класс:
public class TestClass
{
public Dictionary<int, string> Dictionary { get; set; }
}
и создать экземпляр этого класса:
var obj = new TestClass
{
Dictionary = new Dictionary<int, string>
{
{1, "First"},
{2, "Second"},
{3, "Other"},
}
};
Вы можете сериализовать этот объект с помощью 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);
Результат xml будет выглядеть так:
<?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>
Вы можете установить ExtendedXmlSerializer из nuget или выполнить следующую команду:
Install-Package ExtendedXmlSerializer
Я использую сериализуемые классы для обмена WCF между различными модулями. Ниже приведен пример сериализуемого класса, который также служит DataContract. Мой подход заключается в использовании возможности LINQ для преобразования Словаря в готовый к сериализации List < > 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; }
}
}
Использование простое - я назначаю словарь в поле словаря объектов данных - DictionaryX. Сериализация поддерживается внутри SerializableClassX путем преобразования назначенного словаря в сериализуемый 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},
};
Существует простой способ использования Sharpeserializer (с открытым исходным кодом):
http://www.sharpserializer.com/
Он может напрямую сериализовать/дезацинировать словарь.
Нет необходимости отмечать свой объект каким-либо атрибутом, и вам не нужно указывать тип объекта в методе Serialize (см. здесь).
Для установки через nuget: Install-package sharpserializer
Тогда это очень просто:
Hello World (с официального сайта):
// 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");
KeyedCollection работает как словарь и сериализуем.
Сначала создайте класс, содержащий ключ и значение:
/// <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;
}
затем создайте класс типа KeyedCollection и определите свойство вашего класса в качестве ключа.
/// <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;
}
}
Полезно для сериализации данных такого типа.