0

I am using DataContractJsonSerializer to serialize/deserialize my object to/from JSON string. My object contains a Dictionary<string, string> data member, and I want to make this dictionary case insensitive after deserialization. Currently, after ReadObject, I manually create a new dictionary explicitly with StringComparer.OrdinalIgnoreCase, and copy the content to the new dictionary. This looks ugly. Is there some way to do this more elegantly? Like controlling the object creation when deserializing?

dbc
  • 104,963
  • 20
  • 228
  • 340
codewarrior
  • 723
  • 7
  • 22

1 Answers1

0

The data contract serializers are able to serialize and deserialize get-only collection properties as long as the returned collection is pre-allocated. (This is in contrast with other types of get-only property, which cannot be serialized directly.) However, since the constructors of data contract objects are not called, it is necessary to pre-allocate somewhere else, e.g. in an OnDeserializing method, or in the getter itself:

[DataContract]
public class RootObject
{
    Dictionary<string, string> dictionary;

    [DataMember]
    public Dictionary<string, string> Dictionary
    {
        get
        {
            if (dictionary == null)
                System.Threading.Interlocked.CompareExchange(ref dictionary, new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase), null);
            return dictionary;
        }
        // DO NOT ADD A SET METHOD EVEN IF PRIVATE, it will break data contract serialization.
    }
}

The Dictionary data member will now be deserialized into your pre-allocated Dictionary<string, string> that uses StringComparer.OrdinalIgnoreCase.

Notes:

  • There cannot be a set method, even one that is private. If there is, the data contract serializers will construct their own Dictionary<string, string> and set it back, ignoring the pre-allocated dictionary.

  • Since the default constructors for collection data contract objects are called, you could instead subclass Dictionary<string, TValue> and set an appropriate comparer in the constructor, e.g. as follows:

    public class OrdinalIgnoreCaseDictionary<TValue> : Dictionary<string, TValue>
    {
        public OrdinalIgnoreCaseDictionary() : base(StringComparer.OrdinalIgnoreCase) { }
    }
    
    [DataContract]
    public class RootObject
    {
        [DataMember]
        public OrdinalIgnoreCaseDictionary<string> Dictionary { get; set; }
    }
    
dbc
  • 104,963
  • 20
  • 228
  • 340