空のDictionary<int, string>
を使用して、XMLのキーと値を入力する方法
<items>
<item id='int_goes_here' value='string_goes_here'/>
</items>
xElementを使用せずにシリアル化してXMLに戻しますか?
一時的な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);
気が変わった場合は、次のようにしますsing 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"));
Paul Welterの 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
}
まず、このコードには1つの落とし穴があります。これがある別のソースから辞書を読んだとしましょう:
<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>
辞書には1つのキーしか持てないため、これは分離解除時に例外をスローします。
直列化された辞書でXElementを使用する必要がある理由は、辞書がDictionary<String,String>
として定義されておらず、辞書がDictionary<TKey,TValue>
であるためです。
問題を確認するには、自分に問いかけます。XMLとしてそれ自体を記述する要素を使用するものにシリアル化するTValue
があるとします(辞書の辞書Dictionary<int,Dictionary<int,string>>
(パターン、それはルックアップテーブルです))、あなたの属性のみのバージョンはどのように完全に属性内の辞書を表しますか?
ディクショナリはデフォルトではC#ではSerializableではありません。その理由はわかりませんが、デザインの選択であったようです。
今のところ、 Json.NET を使用してJSONに変換し、そこから辞書に変換することをお勧めします(逆も同様)。 XMLが本当に必要でない限り、JSONを完全に使用することをお勧めします。
構造体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, "") + "]";
}
}
次に、Dictionary
プロパティのXMLシリアル化は次のとおりです。
[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);
}
}
プロパティだけがリストではなく配列でなければなりません。
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());
}
}
}
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
クラスBの配列を含むクラスAを記述します。クラスBにはidプロパティとvalueプロパティが必要です。 xmlをクラスAにデシリアライズします。Aの配列を目的の辞書に変換します。
辞書をシリアル化するには、クラスAのインスタンスに変換し、シリアル化します...
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;
}
}
この種のデータをシリアル化するのに便利です。
異なるモジュール間のWCF通信にシリアル化可能なクラスを使用します。以下は、DataContractとしても機能するシリアル化可能なクラスの例です。私のアプローチは、LINQの機能を使用して、辞書をすぐに使用可能なKeyValuePair <>のシリアル化可能なList <>に変換することです。
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に辞書を割り当てます。シリアル化は、割り当てられた辞書をKeyValuePair <>のシリアル化可能なList <>に変換することにより、SerializableClassX内でサポートされます。
// 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メソッドでオブジェクトタイプを指定する必要もありません( here を参照)。
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");