DataContractSerializer requires parameterless constructor in abstract base class
Asked Answered
N

1

6

When I have the following classes and I try to serialize a ConcreteClass instance with DataContractSerializer.WriteObject(..) I get an InvalidDataContractException.

public abstract class AbstractClass
{            
  protected AbstractClass(string text) { }
}

public class ConcreteClass : AbstractClass
{
  public ConcreteClass() : base("text") {  } 
}

The serializer is instantiated with new DataContractSerializer(typeof(ConcreteClass).

Using XmlSerializer makes no problems.

Now when adding public AbstractClass() {}

both serializers work.

So why does the DataContractSerializer requires abstract base classes to have a parameterless contructor? Here it is stated that types can be serialized which "have a constructor that does not have parameters" which is true for ConcreteClass. I also added some code to this required constructor and i don't think that it is ever called during the serialization process.


The complete Exception says:

System.Runtime.Serialization.InvalidDataContractException : Type AbstractClass' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. See the Microsoft .NET Framework documentation for other supported types.

It even works if i leave the parameterless constructor and instead use the proposed attribute. So why is there a difference and why there is an attempt to serialize an abstract class? Of course there could be things like properties in the abstract class but should't these be serialized together with a ConcreteClass instance (which inherit such things)?

Edit:

My exact code:

namespace SerilizationTest
{
  public abstract class AbstractClass
  {
    public string StringProperty { get; set; }

    //This constructor is required (although never called).
    //If not present we get "InvalidDataContractException :
    //Type AbstractClass cannot be serialized"
    public AbstractClass()
    {
      Console.WriteLine("We won't see this.");
    }

    public AbstractClass(string text)
    {
      StringProperty = text;
    }
  }

  public class ConcreteClass : AbstractClass
  {
    public ConcreteClass() : base("text") { }
  }

  class Program
  {
    static void Main()
    {
      var serializer = new DataContractSerializer(typeof(ConcreteClass));
      var memStream = new MemoryStream();
      serializer.WriteObject(memStream, new ConcreteClass());
      memStream.Seek(0, SeekOrigin.Begin);
      var deserializedObj = (ConcreteClass)serializer.ReadObject(memStream);
      Console.WriteLine(deserializedObj.StringProperty);
    }
  }
}
Nightshade answered 10/6, 2011 at 10:52 Comment(6)
How are you planning on instantiating an abstract class? Logical error, does not compute.Louisville
I don't plan that. When I wrote "adding public AbstractClass() {}" i meant adding this line of code into the code from the AbstractClassNightshade
Did you ever find an answer to this?Cat
@Cat Not really, except the obvious one (having no constructors with parameters in the base class, making the fields in the base class at least protected and non-readonly, and then initialize those fields from the sub constructor.)Nightshade
@Nightshade I ended up switching to Json.Net just to work around this... Thanks!Cat
I'd be tempted to write my own serializer - this one also is unable to identify constructors with parameters that correspond to properties (thus forcing you to create mutable properties). One tip I can share though is that the parameterless constructor can be made private to at least reduce the potential abuse of it and the serializer can still use it.Vinic
U
-3

The exception you get is saying that there is no [DataContract] attribute on the AbstractClass.

DataContract is an attribute that is used to mark classes that can be serializable. This attribute will only included attributes in your class to be serialized if you tell it to do so by using the DataMember attribute.

The DataMember attribute is used to mark which attributes you want in your serializable class.

This attribute is found at System.Runtime.Serialization;

For example...

public abstract class Bar
{
}

public class Foo : Bar
{
    string one { get; set; }
    string two { get; set; }
    string three { get; set; }
}

If I try to serialize my Foo class then I will get your exeption. So if I add the DataContract attribute to my Bar class like the exception suggests the next time I try to serialize I will get the same error just pointing at another part of my code, the Foo class itself. What I need to do is add DataContract to both like so.

[DataContract]
public abstract class Bar
{
}

[DataContract]
public class Foo : Bar
{
    string one { get; set; }
    string two { get; set; }
    string three { get; set; }
}

So now what we have will be able to create a serialized file. However, there won't be any information as we never told our DataContract what to include.To fix this problem we add the DataMember attribute to the attributes in the class we want to include.

[DataContract]
public class Foo : Bar
{
    [DataMember]
    string one { get; set; }
    string two { get; set; }
    [DataMember]
    string three { get; set; }
}

With the DataMember attributes added when this class is serialized it will only serialize the information about string one and string three. string two will not be included because it wasn't specifically marked with the DataMember attribute.

Underlaid answered 23/7, 2012 at 23:10 Comment(2)
Your solution works. My problem wasn't to get it to work somehow but to find out why the abstract base class needs a parameterless constructor. I'll edit in my exact code to make my question clearer. Btw you don't need the attributes.Nightshade
-1 This answer is both irrelevant to the question and incorrect. Contrary to popular belief - [DataContract] attribute is optional. When missing - the DataContractSerializer will infer the data contract of the class as "All public read/write properties and fields of the type". See msdn.microsoft.com/en-us/library/ms733127.aspxCajeput

© 2022 - 2024 — McMap. All rights reserved.