このブログエントリSerializableDictionary
で定義されているデータを保存し、WCFサービスとの間でデータをやり取りしています。値タイプを値として使用する場合、これは正常に機能します。これは、値タイプを簡単にボックス化してsにすることができるためです。しかし、私が次のようなものを使用する場合<string, object>
object
new List<int>() { 5, 10 }
、例外が発生します:
タイプSystem.Collections.Generic.List`1[[System.Int32、mscorlib、Version = 4.0.0.0、Culture = neutral、PublicKeyToken=b77a5c561934e089]]はこのコンテキストでは使用できません。
ここでの説明によると、 ;value.GetType()
を初期化するために使用する必要があります。XmlSerializer
ただし、これでシリアル化できますが、一般的な方法で逆シリアル化する方法がわかりませんSerializableDictionary
。
タイプ引数として許可しながらこれをきれいに変更する方法があるかどうかはわかりません<string, object>
-値をXMLではなくバイナリにシリアル化してその方法で転送できます(同じコードがシリアル化と逆シリアル化されているので、私はそうではありません相互運用性が心配です)が、可能であればXMLを使用したいと思います。
編集
完全なコード例:
XmlSerializer serializer = new XmlSerializer(typeof(SerializableDictionary<string, object>));
SerializableDictionary<string, object> dic = new SerializableDictionary<string, object>();
dic["test"] = new List<int>() { 5, 10 };
StringBuilder sb = new StringBuilder();
XmlWriter writer = XmlWriter.Create(sb);
serializer.Serialize(writer, dic);
string ser = sb.ToString();
解決
正しい答えをくれたNicoSchertlerに感謝します。誰かがそれを必要とする場合に備えて、私はここに私の最終的なコードを投稿しています。これは最初のリンクの元のコードと下位互換性があるため、そのコードによってシリアル化されたものはすべて、以下によって逆シリアル化できます。
[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{
#region " IXmlSerializable Members "
#region " WriteXml "
public void WriteXml(XmlWriter writer)
{
// Base types
string baseKeyType = typeof(TKey).AssemblyQualifiedName;
string baseValueType = typeof(TValue).AssemblyQualifiedName;
writer.WriteAttributeString("keyType", baseKeyType);
writer.WriteAttributeString("valueType", baseValueType);
foreach (TKey key in this.Keys)
{
// Start
writer.WriteStartElement("item");
// Key
Type keyType = key.GetType();
XmlSerializer keySerializer = GetTypeSerializer(keyType.AssemblyQualifiedName);
writer.WriteStartElement("key");
if (keyType != typeof(TKey)) { writer.WriteAttributeString("type", keyType.AssemblyQualifiedName); }
keySerializer.Serialize(writer, key);
writer.WriteEndElement();
// Value
TValue value = this[key];
Type valueType = value.GetType();
XmlSerializer valueSerializer = GetTypeSerializer(valueType.AssemblyQualifiedName);
writer.WriteStartElement("value");
if (valueType != typeof(TValue)) { writer.WriteAttributeString("type", valueType.AssemblyQualifiedName); }
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
// End
writer.WriteEndElement();
}
}
#endregion
#region " ReadXml "
public void ReadXml(XmlReader reader)
{
bool wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
{
return;
}
// Base types
string baseKeyType = typeof(TKey).AssemblyQualifiedName;
string baseValueType = typeof(TValue).AssemblyQualifiedName;
while (reader.NodeType != XmlNodeType.EndElement)
{
// Start
reader.ReadStartElement("item");
// Key
XmlSerializer keySerializer = GetTypeSerializer(reader["type"] ?? baseKeyType);
reader.ReadStartElement("key");
TKey key = (TKey)keySerializer.Deserialize(reader);
reader.ReadEndElement();
// Value
XmlSerializer valueSerializer = GetTypeSerializer(reader["type"] ?? baseValueType);
reader.ReadStartElement("value");
TValue value = (TValue)valueSerializer.Deserialize(reader);
reader.ReadEndElement();
// Store
this.Add(key, value);
// End
reader.ReadEndElement();
reader.MoveToContent();
}
reader.ReadEndElement();
}
#endregion
#region " GetSchema "
public XmlSchema GetSchema()
{
return null;
}
#endregion
#endregion
#region " GetTypeSerializer "
private static readonly Dictionary<string, XmlSerializer> _serializers = new Dictionary<string, XmlSerializer>();
private static readonly object _deadbolt = new object();
private XmlSerializer GetTypeSerializer(string type)
{
if (!_serializers.ContainsKey(type))
{
lock (_deadbolt)
{
if (!_serializers.ContainsKey(type))
{
_serializers.Add(type, new XmlSerializer(Type.GetType(type)));
}
}
}
return _serializers[type];
}
#endregion
}
XMLの長さを抑えるために、基本型とは異なる場合にのみ型を書き出します。また、 XmlSerializer
sの静的リストを保持して、それらがいたるところに存在しないようにします。各ノードで型が書き出されないようにするために、最初に提供された型を書き出す必要がありました。