Why XML-Serializable class need a parameterless constructor
Asked Answered
T

6

182

I'm writing code to do Xml serialization. With below function.

public static string SerializeToXml(object obj)
{
    XmlSerializer serializer = new XmlSerializer(obj.GetType());
    using (StringWriter writer = new StringWriter())
    {
        serializer.Serialize(writer, obj);
        return writer.ToString();
    }
}

If the argument is a instance of class without parameterless constructor, it will throw a exception.

Unhandled Exception: System.InvalidOperationException: CSharpConsole.Foo cannot be serialized because it does not have a parameterless constructor. at System.Xml.Serialization.TypeDesc.CheckSupported() at System.Xml.Serialization.TypeScope.GetTypeDesc(Type type, MemberInfo sourc e, Boolean directReference, Boolean throwOnError) at System.Xml.Serialization.ModelScope.GetTypeModel(Type type, Boolean direct Reference) at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type , XmlRootAttribute root, String defaultNamespace) at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultName space) at System.Xml.Serialization.XmlSerializer..ctor(Type type)

Why must there be a parameterless constructor in order to allow xml serialization to succeed?

EDIT: thanks for cfeduke's answer. The parameterless constructor can be private or internal.

Thiel answered 6/11, 2008 at 5:33 Comment(3)
If you're interested, I found how to create objects without needing the constructor (see update) - but this won't help XmlSerializer at all - it still demands it. Useful for custom code, maybe.Effervescent
XmlSerializer requires a default parameterless constructor for deserialization.Ultann
The declaration of the parameterless constructor is not required. Once a parameterized constructor is declared, the parameterless constructor must also be declared.Parasynapsis
A
262

During an object's de-serialization, the class responsible for de-serializing an object creates an instance of the serialized class and then proceeds to populate the serialized fields and properties only after acquiring an instance to populate.

You can make your constructor private or internal if you want, just so long as it's parameterless.

Ancohuma answered 6/11, 2008 at 5:37 Comment(8)
Oh, so, I can make the parameterless ctor private or internal and the serialization still works. Thanks for your answer.Thiel
Yes I do it often, though I've come to accept that public parameterless constructors are great because they allow you to use "new()" with generics and the new initialization syntax. For parametered constructors use static factory methods or the builder pattern implementation.Ancohuma
The accessibility tip is a good one, but your explanation makes no sense for serialization. An object needs to be created only for de-serialization. I'd hazard a guess that the type-check code is built into the XmlSerializer constructor because a single instance can be used both ways.Substage
Oh, I'll s/serialization/deserialization/ to make it clearer as to what I mean (was referring to serialization/deserialization as just "serialization" as an all encompassing term which isn't exactly correct and may lead to confusion).Ancohuma
@TomerGabel, what would be the point of serializing without deserializing? The type check exists to make sure when serializing that deserialization is possible.Venipuncture
@Venipuncture One example is when you're sending your XML to a web service of some sort and are not interested in receiving those objects in your own component.Substage
Keep in mind that even if you make your parameterless constructor private or internal, all of your properties whose values were serialized must have public setters.Acalia
How to overcome the situation where you already have parameterless constructor for normal construction in this fashion public DerivedClass() : base(null) { }Helper
E
77

This is a limitation of XmlSerializer. Note that BinaryFormatter and DataContractSerializer do not require this - they can create an uninitialized object out of the ether and initialize it during deserialization.

Since you are using xml, you might consider using DataContractSerializer and marking your class with [DataContract]/[DataMember], but note that this changes the schema (for example, there is no equivalent of [XmlAttribute] - everything becomes elements).

Update: if you really want to know, BinaryFormatter et al use FormatterServices.GetUninitializedObject() to create the object without invoking the constructor. Probably dangerous; I don't recommend using it too often ;-p See also the remarks on MSDN:

Because the new instance of the object is initialized to zero and no constructors are run, the object might not represent a state that is regarded as valid by that object. The current method should only be used for deserialization when the user intends to immediately populate all fields. It does not create an uninitialized string, since creating an empty instance of an immutable type serves no purpose.

I have my own serialization engine, but I don't intend making it use FormatterServices; I quite like knowing that a constructor (any constructor) has actually executed.

Effervescent answered 6/11, 2008 at 8:2 Comment(9)
Thanks for the tip about FormatterServices.GetUninitializedObject(Type). :)Consequently
Heh; turns out that I don't follow my own advice; protobuf-net has (optionally) allowed FormatterServices usage for agesEffervescent
But what I don't understand is, in the case no constructor is specified, the compiler creates a public parameterless constructor. So why isn't that good enough for the xml deserialization engine?Swinge
If I want to deserialize XML and initialize certain object using their constructor (so that the elements/attributes are provided via the constructor), is there ANY way to achieve this? Isn't there a way to customize the serialization process so that it builds the objects using their constructors?Damalas
@Shimmy nope; that's not supported. There is IXmlSerializable, but a: that happens after the constructor, and b: it is very ugly and hard to get right (especially deserialization) - I strongly recommend against trying to implement that, but : it won't allow you to use constructorsEffervescent
Thanks. Any other means (besides XML) that allows deserialization via constructors?Damalas
What are the contents supposed to be for the parameterless constructor meant to satisfy XmlSerializer in the deserialization process? Since people wrote some answers here but nobody mentioned the contents of the constructor presumably the contents of the constructor doesn't matter. Suppose I have some complicated stuff in there so it gets executed but then it gets overwritten anyway by the object tree implied by the XML file? If my interpretation is correct then the parameterless constructor only serves to make some space in memory. Was it silly of .NET to execute that code?Parasynapsis
@Parasynapsis you need to create an instance somehow. There is a way of doing it without invoking a constructor, but using a constructor allows for things like list initializers.Effervescent
I created a space to describe the rules for the contents of the parameterless constructor. #66394677Parasynapsis
M
11

The answer is: for no good reason whatsoever.

Contrary to its name, the XmlSerializer class is used not only for serialization, but also for deserialization. It performs certain checks on your class to make sure that it will work, and some of those checks are only pertinent to deserialization, but it performs them all anyway, because it does not know what you intend to do later on.

The check that your class fails to pass is one of the checks that are only pertinent to deserialization. Here is what happens:

  • During deserialization, the XmlSerializer class will need to create instances of your type.

  • In order to create an instance of a type, a constructor of that type needs to be invoked.

  • If you did not declare a constructor, the compiler has already supplied a default parameterless constructor, but if you did declare a constructor, then that's the only constructor available.

  • So, if the constructor that you declared accepts parameters, then the only way to instantiate your class is by invoking that constructor which accepts parameters.

  • However, XmlSerializer is not capable of invoking any constructor except a parameterless constructor, because it does not know what parameters to pass to constructors that accept parameters. So, it checks to see if your class has a parameterless constructor, and since it does not, it fails.

So, if the XmlSerializer class had been written in such a way as to only perform the checks pertinent to serialization, then your class would pass, because there is absolutely nothing about serialization that makes it necessary to have a parameterless constructor.

As others have already pointed out, the quick solution to your problem is to simply add a parameterless constructor. Unfortunately, it is also a dirty solution, because it means that you cannot have any readonly members initialized from constructor parameters.

In addition to all this, the XmlSerializer class could have been written in such a way as to allow even deserialization of classes without parameterless constructors. All it would take would be to make use of "The Factory Method Design Pattern" (Wikipedia). From the looks of it, Microsoft decided that this design pattern is far too advanced for DotNet programmers, who apparently should not be unnecessarily confused with such things. So, DotNet programmers should better stick to parameterless constructors, according to Microsoft.

Manis answered 2/11, 2019 at 19:57 Comment(2)
Lol you say, For no good reason whatsoever, then go on to say, XmlSerializer is not capable of invoking any constructor except a parameterless constructor, because it does not know what parameters to pass to constructors that accept parameters. If it doesn't know what parameters to pass to a constructor, then how would it know what parameters to pass to a factory? Or which factory to use? I can't imagine this tool being more straightforward to use - you want a class deserialized, then let the deserializer make a default instance and then populate each field you tagged. Easy.Expertize
@Expertize you might want to ask another StackOverflow question "how does a factory method help me with constructor parameters" and let me know, I will answer it for you.Manis
B
3

Seems nobody actually read the original post... it is about SERIALIZATION and not DE-... and for this, no constructors are needed or called at all. The issue is simply poor coding practice from Microsoft.

Bash answered 20/5, 2022 at 6:15 Comment(0)
M
0

First of all, this what is written in documentation. I think it is one of your class fields, not the main one - and how you want deserialiser to construct it back w/o parameterless construction ?

I think there is a workaround to make constructor private.

Mincey answered 6/11, 2008 at 5:47 Comment(0)
P
0

I have this issue for error for serialization

I add a parameterless constructor to class and it's solved.

public Product(int code, string name) { Code = code; Name = name;//constructor elements }

public Product() {///// }

Pontificate answered 15/12, 2023 at 2:28 Comment(1)
This does not really answer the question. If you have a different question, you can ask it by clicking Ask Question. To get notified when this question gets new answers, you can follow this question. Once you have enough reputation, you can also add a bounty to draw more attention to this question. - From ReviewAffiliation

© 2022 - 2024 — McMap. All rights reserved.