Search code examples
c#.netgenericsdatacontractserializer

DataContractSerializer and Dictionary<string,object> fails when reading


I'm using DataContractSerializer to serialize an object that contains a Dictionary<string,object> member, which is marked with [DataMember()]. The idea is to have a flexible bag of object attributes, and I don't know what those attributes could be.

This works great when I'm putting int, double and string objects into the dictionary, but when I put a List<string> in it fails to deserialize the object with:

System.InvalidOperationException: Node type Element is not supported in this operation.

The entire dictionary is serialized to XML, and it looks pretty reasonable:

<Attributes xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <d2p1:KeyValueOfstringanyType>
        <d2p1:Key>name</d2p1:Key>
        <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:string">Test object</d2p1:Value>
    </d2p1:KeyValueOfstringanyType>
    <d2p1:KeyValueOfstringanyType>
        <d2p1:Key>x</d2p1:Key>
        <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:double">0.5</d2p1:Value>
    </d2p1:KeyValueOfstringanyType>
    <d2p1:KeyValueOfstringanyType>
        <d2p1:Key>y</d2p1:Key>
        <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:double">1.25</d2p1:Value>
    </d2p1:KeyValueOfstringanyType>
    <d2p1:KeyValueOfstringanyType>
        <d2p1:Key>age</d2p1:Key>
        <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:int">4</d2p1:Value>
    </d2p1:KeyValueOfstringanyType>
    <d2p1:KeyValueOfstringanyType>
        <d2p1:Key>list-of-strings</d2p1:Key>
        <d2p1:Value>
            <d2p1:string>one string</d2p1:string>
            <d2p1:string>two string</d2p1:string>
            <d2p1:string>last string</d2p1:string>
        </d2p1:Value>
    </d2p1:KeyValueOfstringanyType>
</Attributes>

Note the list-of-strings at the end there. It's got all the values but nothing indicating that it's a List<string> or anything.

What's the correct way of handling this situation?


Solution

  • Try using the KnownTypeAttribute so that DataContractSerializer knows about the List<string> type. Unfortunately, that seems to go against your idea of not having to know about the types before hand.

    I'm basing this on the following code, which uses DataContractSerializer to serialize a Dictionary<string, object> containing List<string>:

    Dictionary<string,object> dictionary = new Dictionary<string, object>();
    
    dictionary.Add("k1", new List<string> { "L1", "L2", "L3" });
    
    List<Type> knownTypes = new List<Type> { typeof(List<string>) };
    DataContractSerializer serializer = new DataContractSerializer(typeof(Dictionary<string,object>), knownTypes);
    MemoryStream stream = new MemoryStream();
    
    serializer.WriteObject(stream, dictionary);
    
    StreamReader reader = new StreamReader(stream);
    
    stream.Position = 0;
    string xml = reader.ReadToEnd();
    

    If you knownTypes is not provided to the DataContractSerializer, it throws an exception.

    SerializationException: Type 'System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' with data contract name 'ArrayOfstring:http://schemas.microsoft.com/2003/10/Serialization/Arrays' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.