Search code examples
c#xmlwcfdictionarydatacontractserializer

Deserializing xml Dictionary<TKey,TValue> with DataContactSerializer


I am trying to Deserialize a Dictionary<TKey,TValue> with a DataContractSerializer. I am not getting an exception, my dictionary is coming back null. I have read over multiple posts, tried multiple implementations and still have found no success. I'm sure it is something really small that I am missing and could really use few sets of eyes to look over this for me and maybe pick it apart and tell me where my problem is. Any help is greatly appreciated. The classes I'm using are as follows:

[DataContract]
public class Config
{
    public Config()
    {
        SerializableDictionary = new SerializableDictionary<int, string>();
    }

    [DataMember]
    public SerializableDictionary<int, string> SerializableDictionary { get; set; }
}

[CollectionDataContract(Name = "SerializableDictionary", ItemName = "DictionaryItem", KeyName = "Key", ValueName = "Value")]
public sealed class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{

}

my XML:

<?xml version="1.0" encoding="utf-8"?>
<Config xmlns="http://TestNameSpace" >
  <SerializableDictionary >
    <DictionaryItem>
      <Key>1</Key>
      <Value>one</Value>
    </DictionaryItem>
    <DictionaryItem>
      <Key>2</Key>
      <Value>two</Value>
    </DictionaryItem>
    <DictionaryItem>
      <Key>3</Key>
      <Value>three</Value>
    </DictionaryItem>
    <DictionaryItem>
      <Key>4</Key>
      <Value>four</Value>
    </DictionaryItem>
    <DictionaryItem>
      <Key>5</Key>
      <Value>five</Value>
    </DictionaryItem>
    <DictionaryItem>
      <Key>6</Key>
      <Value>six</Value>
    </DictionaryItem>
  </SerializableDictionary>
</Config>

Calling Method:

private static void Main(string[] args)
{
    Config config = null;
    using (var stream = new FileStream(
        Path.GetDirectoryName(
            Assembly.GetExecutingAssembly()
                    .Location) + "\\Dictionary.xml", FileMode.Open))
    using (var reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas()))
    {
        var serializer = new DataContractSerializer(typeof(Config), "Config", "http://TestNameSpace");

        config = (Config)serializer.ReadObject(reader);
    }

    foreach (var obj in config.SerializableDictionary)
        Console.WriteLine(obj.Value);

}

Solution

  • In order to figure this out, I created an Encode method that encodes using the same DataContract as you created for the Config class.

    public static void Encode()
    {
      var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\dataOut.xml";
      using (var stream = new FileStream(path, FileMode.OpenOrCreate))
      {
        var serializer = new DataContractSerializer(typeof(Config), "Config", "http://TestNameSpace");
    
        serializer.WriteObject(stream, new Config(){SerializableDictionary = new SerializableDictionary<int, string> { { 1, "hello" }, { 2, "world"} }});
      }
    }
    

    After looking at the output, it was clear that the problem was a namespace issue for the child type.

    <Config xmlns="http://TestNameSpace" xmlns:a="http://schemas.datacontract.org/2004/07/DeserializeDictionary" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <a:SerializableDictionary>
        <a:DictionaryItem>
          <a:Key>1</a:Key>
          <a:Value>hello</a:Value>
        </a:DictionaryItem>
        <a:DictionaryItem>
          <a:Key>2</a:Key>
          <a:Value>world</a:Value>
        </a:DictionaryItem>
      </a:SerializableDictionary>
    </Config>
    

    I first added the Namespace = "http://TestNameSpace" to the SerializableDictionary's CollectionDataContract, but that didn't solve it. Only after I also added the Namespace = "http://TestNameSpace" to Config's DataContract did it serialize the way you created your XML file.

    Here is the updated Config and SerializableDictionary that got it to work for me:

    [DataContract(Namespace = "http://TestNameSpace")]
    public class Config
    {
      public Config()
      {
        SerializableDictionary = new SerializableDictionary<int, string>();
      }
    
      [DataMember]
      public SerializableDictionary<int, string> SerializableDictionary { get; set; }
    }
    
    [CollectionDataContract(Name = "SerializableDictionary", ItemName = "DictionaryItem", KeyName = "Key", ValueName = "Value", Namespace = "http://TestNameSpace")]
    public sealed class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>
    {
    
    }