古事連記帖

趣味のこと、技術的なこと、適当につらつら書きます。

DictionaryクラスをSerializableにする

.NET FrameworkのDictionaryクラスは訳あってXmlSerializerでシリアライズできません。
ただし、IXmlSerializableインターフェースを実装したDictionaryクラスを作ればどうにかできるのは、他の人もやられていました。
C#::DictionaryをXMLSerializerでシリアライズしたいんですが? - 虚飾の王
C#::DictionaryをXMLSerializerでシリアライズしたいんですが?(2) - 虚飾の王
rarirurero: シリアライズ可能なDictionary(C#)


ただ、時々変なところでデシリアライズできないことがあり、デシリアライズできないままnullの状態でアクセスしてしまいNullReferenceExceptionを吐いてしまうので、そこを修正。

public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{
	public System.Xml.Schema.XmlSchema GetSchema()
	{
		return null;
	}

	public void ReadXml(System.Xml.XmlReader reader)
	{
		var serializer = new XmlSerializer(typeof(KeyValueItem));

		reader.Read();
		if (reader.IsEmptyElement)
			return;

		try
		{
			while (reader.NodeType != XmlNodeType.EndElement)
			{
				// これがないと下でぬるりが出る時がある
				if (!serializer.CanDeserialize(reader))
					return;
				else
				{
					var item = serializer.Deserialize(reader) as KeyValueItem; // 従来はここでnullになるときがあった
					this.Add(item.Key, item.Value);
				}
			}
		}
		finally
		{
			reader.Read();
		}
	}

	public void WriteXml(System.Xml.XmlWriter writer)
	{
		var ns = new XmlSerializerNamespaces();
		ns.Add(string.Empty, string.Empty);

		var serializer = new XmlSerializer(typeof(KeyValueItem));

		foreach (var item in this.Keys.Select(key => new KeyValueItem(key, this[key])))
		{
			serializer.Serialize(writer, item, ns);
		}
	}

	public class KeyValueItem
	{
		public TKey Key { get; set; }
		public TValue Value { get; set; }

		public KeyValueItem(TKey key, TValue value)
		{
			Key = key;
			Value = value;
		}

		public KeyValueItem()
		{
		}
	}
}

そもそも.NET的にシリアライズできないのには意味があるんだから無理するなっていう感じもしますがー