From the .NET source I see that Dictionary<TKey,TValue>
implements ISerializable
and thus the method: void GetObjectData(SerializationInfo info, StreamingContext context);
.
Looking at GetObjectData()
it appears to me that the Comparer
is indeed added as value to the SerializationInfo
instance, which, according to the MSDN docs; Stores all the data needed to serialize or deserialize an object:
[System.Security.SecurityCritical] // auto-generated_required
public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
...
info.AddValue(VersionName, version);
#if FEATURE_RANDOMIZED_STRING_HASHING
info.AddValue(ComparerName, HashHelpers.GetEqualityComparerForSerialization(comparer), typeof(IEqualityComparer<TKey>));
#else
info.AddValue(ComparerName, comparer, typeof(IEqualityComparer<TKey>));
#endif
info.AddValue(HashSizeName, buckets == null ? 0 : buckets.Length); //This is the length of the bucket array.
...
}
... however, in practice, that depends on whether one uses the DataContractSerializer
or the NetDataContractSerializer
. Here follows a complete example:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
namespace ConsoleApplication2
{
public class Program2
{
public static void Main(string[] args)
{
var dict = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
{"k", 123}
};
// Prints: "OrdinalComparer"
Console.WriteLine(dict.Comparer.GetType().Name);
// Using DataContractSerializer (DCS)
var dcsXml = SerializeWithDcs(dict);
var dcsDict = DeserializeWithDcs<Dictionary<string, int>>(dcsXml);
// Prints: "GenericEqualityComparer`1"
Console.WriteLine(dcsDict.Comparer.GetType().Name);
// Using NetDataContractSerializer (NetDCS)
var netDcsXml = SerializeWithNetDcs(dict);
var netDcsDict = DeserializeWithNetDcs<Dictionary<string, int>>(netDcsXml);
// Prints: "OrdinalComparer"
Console.WriteLine(netDcsDict.Comparer.GetType().Name);
Console.ReadLine();
}
private static string SerializeWithDcs<T>(T obj)
{
using (var ms = new MemoryStream())
{
using (var sr = new StreamReader(ms, Encoding.UTF8))
{
var serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(ms, obj);
ms.Position = 0;
return sr.ReadToEnd();
}
}
}
private static T DeserializeWithDcs<T>(string xml)
{
using (var ms = new MemoryStream())
{
using (var sw = new StreamWriter(ms, Encoding.UTF8))
{
sw.Write(xml);
sw.Flush();
ms.Position = 0;
var deserializer = new DataContractSerializer(typeof(T));
return (T)deserializer.ReadObject(ms);
}
}
}
private static string SerializeWithNetDcs(object obj)
{
using (var ms = new MemoryStream())
{
using (var sr = new StreamReader(ms, Encoding.UTF8))
{
var serializer = new NetDataContractSerializer();
serializer.WriteObject(ms, obj);
ms.Position = 0;
return sr.ReadToEnd();
}
}
}
private static T DeserializeWithNetDcs<T>(string xml)
{
using (var ms = new MemoryStream())
{
using (var sw = new StreamWriter(ms, Encoding.UTF8))
{
sw.Write(xml);
sw.Flush();
ms.Position = 0;
var deserializer = new NetDataContractSerializer();
return (T)deserializer.ReadObject(ms);
}
}
}
}
}
Output XML from DataContractSerializer
:
<ArrayOfKeyValueOfstringint xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<KeyValueOfstringint>
<Key>k</Key>
<Value>123</Value>
</KeyValueOfstringint>
</ArrayOfKeyValueOfstringint>
Output XML from NetDataContractSerializer
:
<ArrayOfKeyValueOfstringint z:Id="1" z:Type="System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]" z:Assembly="0" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Version z:Id="2" z:Type="System.Int32" z:Assembly="0" xmlns="">1</Version>
<Comparer z:Id="3" z:Type="System.OrdinalComparer" z:Assembly="0" xmlns="">
<_ignoreCase xmlns="http://schemas.datacontract.org/2004/07/System">true</_ignoreCase>
</Comparer>
<HashSize z:Id="4" z:Type="System.Int32" z:Assembly="0" xmlns="">3</HashSize>
<KeyValuePairs z:Id="5" z:Type="System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]][]" z:Assembly="0" z:Size="1" xmlns="">
<KeyValuePairOfstringint xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
<key z:Id="6">k</key>
<value>123</value>
</KeyValuePairOfstringint>
</KeyValuePairs>
</ArrayOfKeyValueOfstringint>
It is obvious from the XML why the Comparer
is gone when serializing/deserializing with the DataContractSerializer
- it is simply not in the XML.
Adding the type to the DataContractSerializer
's known types, does not make any difference.
I have seen similar questions ( XML serialization of a Dictionary with a custom IEqualityComparer, How do you get WCF serialization to preserve a non-default Comparer on a generic Dictionary? ) but as far as I can tell, no one really addresses the reason behind this behavior, and I would like to understand why the DataContractSerializer
skips GetObjectData()
and thus serialization of the Comparer
?