Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WCF List<string > serialization/deserialization error

I have the following ServiceContract and DataContract classes:

[ServiceContract] 
public interface IWcfService 
{ 
    [OperationContract] 
    Response GetData(); 
} 

[DataContract]
public class Response 
{ 
    [DataMember] 
    public Dictionary<string, object> Data { get; set; } 
} 

When the value of Response.Data dictionary is of type int, string, double or any other 'simple' primitive types, WCF can successfully serialize the object. But when the value of Response.Data dictionary is of type List< string>, the client threw following exception when itreceived the data and tried to deserialize it:

Message=The formatter threw an exception while trying to deserialize the message: 
There was an error while trying to deserialize parameter http://tempuri.org/:GetDataResult. 
The InnerException message was 'Error in line 1 position 990. 
    Element 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:Value' contains data from a type 
    that maps to the name 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:ArrayOfstring'. 
    The deserializer has no knowledge of any type that maps to this name. 
    Consider using a DataContractResolver or add the type corresponding to 'ArrayOfstring' 
    to the list of known types - for example, by using the KnownTypeAttribute attribute or 
    by adding it to the list of known types passed to DataContractSerializer.'.

I've also tried to add KnownType attribute to the ServiceContract and DataContract like following:

[ServiceContract] 
[ServiceKnownType(typeof(List<string>))] 
[ServiceKnownType(typeof(Dictionary<string, string>))] 
[ServiceKnownType(typeof(Dictionary<string, List<string>>))] 
public interface IWcfService 
{ 
    [OperationContract] 
    [ServiceKnownType(typeof(List<string>))] 
    [ServiceKnownType(typeof(Dictionary<string, string>))] 
    [ServiceKnownType(typeof(Dictionary<string, List<string>>))] 
    Response GetData(); 
} 

[DataContract] 
[ServiceKnownType(typeof(List<string>))] 
[ServiceKnownType(typeof(Dictionary<string, string>))] 
[ServiceKnownType(typeof(Dictionary<string, List<string>>))] 
[KnownType(typeof(List<string>))] 
[KnownType(typeof(Dictionary<string, string>))] 
[KnownType(typeof(Dictionary<string, List<string>>))] 
public class Response 
{ 
    [DataMember] 
    public Dictionary<string, object> Data { get; set; } 
} 

But none of this helped. Anyone has any ideas on this?

Updated

The Data would look like:

Data = new new DIctionary<string, object> 
       { 
           {"_id", 12344}, 
           {"names", new List<string>{ "John", "Peter", "Jack"}}, 
           {"time", DateTime.Now} 
       }

The reason why we used Dictionary< string, object>: Server needs to send to the client a dictionary of 'dynamic' data, which can be int, List, DataTime, etc. It will help revolve this issue by using Dictionary, but it also lose the original type information. For example, the client needs List and do some data binding to display the collection, so List.ToString() will not be helpful in this case.

like image 742
wd113 Avatar asked Oct 21 '13 10:10

wd113


3 Answers

I think by Default the System.Object is not Serializable. You need to send Extra information, but it still not the best practice, I recommend define a separate type before using your dictionary.

Refer to the below:

WCF service returning an array of dictionary

WCF System.Object Serialization

Serializing IDictionary in WCF

like image 197
Muhammad Hani Avatar answered Sep 29 '22 19:09

Muhammad Hani


Bear in mind that your data contract gets serialized in to strings over the wire, so any object that you pass over must be serializable (which object is not).

The problem is that you using Object-Orientated patterns in a service orientated context - expecting everything you pass in your dictionary value to be polymorphic to object. I answered a question that had similar "smells" here: https://stackoverflow.com/a/19445875/2382536.

The problem is attributable to the fact that WCF is so good at abstracting away the underlying channel of your service, you are tempted to forget that you are now working in a "Service Orientated" context (KnownTypes is a hack - giving the illusion of OO over the wire). A data contract forms part of the "public" API to your service, and as such must be a clear explicit representation of the data your service exposes. Having a data structure that returns "dynamic" data violates this important rule.

The client needs to know what data it is getting back, so if you want to implement a "dynamic" response, you should implement (say) different methods/endpoints/services for the variations in the response. Within your service there is no reason why you cannot use polymorphism to simplify your code - but this shouldn't leak out in to the public service/data contracts.

like image 21
Lawrence Avatar answered Sep 29 '22 19:09

Lawrence


Thanks for everyone's inputs. I've managed to sovle the issue by configuring WCF to use NetDataContractSerializer as the serializer (default one is DataContractSerializer). NetDataContractSerializer will include more CLR type information when doing serialization, although it has performance hit (roughly double the serialization time).

like image 30
wd113 Avatar answered Sep 29 '22 20:09

wd113