Search code examples
c#.netarraylistdatacontractserializer

Why can't I deserialize a KnownType'd anyType?


I'm trying to use DataContractSerializer outside of WCF to serialize an object. The object in this case inherits from an old generic wrapper around CollectionBase e.g.

[KnownType(typeof(Foo)]
[CollectionDataContract]
class FooCollection : MyCollectionBase<Foo>

[KnownType(typeof(FooCollection)]
[KnownType(typeof(Foo)]
[CollectionDataContract]
class MyCollectionBase<T> : CollectionBase

[DataContract]
class Foo
{
    [DataMember]
    string Name;
    [DataMember]
    string Value;
}

When this is serialized, I'm getting the following structure:

<FooCollection xmlns="http://schemas.datacontract.org/ ...>
  <anyType>
    <Name>...</Name>
    <Value>...</Value>
  </anyType>
</ArrayOfAnyType>

On deserializing I get the error:

System.Runtime.Serialization.SerializationException : Element anyType from namespace http://schemas.datacontract.org/2004/07/MyAssembly cannot have child contents to be deserialized as an object. Please use XmlNode[] to deserialize this pattern of XML.

----> System.Xml.XmlException : End element 'anyType' from namespace 'http://schemas.datacontract.org/2004/07/MyAssembly' expected. Found element 'Name' from namespace 'http://schemas.datacontract.org/2004/07/MyAssembly'. Line 1, position xxx.

Googling this error shows a number of people who changed up their inheritance hierarchy to get the serialization working, or simply list problems with the approach. I haven't been able to find any examples of using XmlNode[] to deserialize this pattern of XML.

So my questions are:

  1. Is there a way to convince the DataContractSerializer that the type stored in the underlying ArrayList is of type Foo?
  2. How do I implement the XmlNode[] workaround?
  3. Is the only solution to use a generic collection that isn't backed by a non-generic one?

Solution

  • I found the cause of the issue, not shown in my original question: MyCollectionBase implemented ISerializable.

    The DataContractSerializer will use an ISerializable implementation before it uses any attributes. It also can't infer a contract from ISerializable, therefore there's no workaround for the anyType deserialization.

    This is why the KnownTypeAttribute wasn't working.