Reputation: 791
I have been getting errors when my Web API 2 solution tries to parse the result when XML is requested.
Come to find out through some more research. Dictionaries have issues when being parsed with the default XML serializer.
A property in a base class parses fine when it looks like this:
public Dictionary<string, CustomObject> MyThings{ get; set; }
This works without all the DataContract and DataSerialization Attributes.
However, if I do the same thing but define the Value Type as 'object' I get the Error:
Consider using a DataContractResolver if you are using DataContractSerializer 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 the serializer.
Which is fine as from what I understand I need to decorate the class with expected types and the serializer should, in theory, serialize the object to XML.
When I decorate the class get the same error.
I don't know if this is because there is still limitations on being able to parse an 'object' type. But from what I have seen in other peoples issues and examples it works if say the property was of type;
object
instead of
IDictionary<string, object>
I'm simply wondering if I am misunderstanding How to use the DataContract, KnownType and/or DataMember attributes or if this is actually still a limitation of parsing Dictionaries with the default XML Serializer.
I have gone through a number of examples and questions here but am coming up short on a good solution.
Has anyone come up with this in the past? If so, how did you get around the limitation?
I have tried using a List of KeyValuePairs but am also failing at that as well.
Thanks!
Example:
[DataContract]
[KnownType(typeof(Dictionary<string, Type_X>))]
[KnownType(typeof(Dictionary<string, Type_Y>))]
public abstract class BaseClass
{
[DataMember]
public Dictionary<string, object> BaseDictionary{ get; set; }
public BaseClass()
{
BaseDictionary = new Dictionary<string, object>();
}
}
Upvotes: 1
Views: 2779
Reputation: 116532
You need to declare Type_X
and Type_Y
as the known types:
[DataContract]
[KnownType(typeof(Type_X))]
[KnownType(typeof(Type_Y))]
public abstract class BaseClass
{
[DataMember]
public Dictionary<string, object> BaseDictionary { get; set; }
public BaseClass()
{
BaseDictionary = new Dictionary<string, object>();
}
}
What you did is to declare Dictionary<string, Type_X>
as a known type, however that type isn't encountered in the object graph. Rather, you need to declare the actual types that will appear as dictionary values, since those cannot be inferred statically.
Having done so, the data contract serializer will generate XML that looks something like:
<ConcreteClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Question41066771">
<BaseDictionary xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:KeyValueOfstringanyType>
<d2p1:Key>X</d2p1:Key>
<d2p1:Value i:type="Type_X">
<X>x</X>
</d2p1:Value>
</d2p1:KeyValueOfstringanyType>
<d2p1:KeyValueOfstringanyType>
<d2p1:Key>Y</d2p1:Key>
<d2p1:Value i:type="Type_Y">
<Y>y</Y>
</d2p1:Value>
</d2p1:KeyValueOfstringanyType>
</BaseDictionary>
</ConcreteClass>
Given the concrete classes:
[DataContract]
public class ConcreteClass : BaseClass
{
}
[DataContract]
public class Type_X
{
[DataMember]
public string X { get; set; }
}
[DataContract]
public class Type_Y
{
[DataMember]
public string Y { get; set; }
}
The i:type
attribute is a w3c standard attribute that allows an element to assert its type. The data contract serializer uses it to indicate the actual contract name of the polymorphic type being serialized. For more, see Data Contract Known Types.
Upvotes: 1