1

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?

Community
  • 1
  • 1
Lasse Christiansen
  • 10,205
  • 7
  • 50
  • 79

0 Answers0