6

I'm working on application in C#, where I need to serialize and deserialize some classes into/from XML. This operations will be implemented in Class Library. I chose XMLSerialization class to serialize/deserialize these classes to XML file. As you know, this implementation uses reflection, which has the condition that the serialized/deserialized class needs to have a parameterless constructor. However, the serialized class has one parameter that is mandatory, which is good candidate as parameter for constructor, to be sure that it is properly initialized. I've created a manager class for serialization with two methods, one to store to file and one to load from file. I thought of multiple solutions:

  1. For serialized class create two constructors, one parameterless and other one with mandatory parameter. This class can be easily serialized, but can be badly initialized, which can cause problems (maybe in other places, for example if class is used in any web service).
  2. As this implementation is in class library, it is possible to create class A (to serialize), which will be public and will have one constructor with mandatory parameter. Managers methods will take as parameter this class A or return class A after is loaded. But internally I would create class B, which will be 1:1 as class A, but with parameterless constructor for serialization or deserialization purposes. This class B will be internal, which means, that it is visible only in current assembly, so it cannot happen, that it can be badly initialized when class library is linked. However, this causes duplicate code. Imagine, that there can be more classes like this and more complex. This means another expense.
  3. Last one solution, which comes to my mind is to implement serialization by myself using Linq to XML. This solution is safe and without duplicate code, but implementation is difficult for big classes. Or even worse, when we want to change element names or switch between elements and attributes, we have to go through this implementation every time carefully and change it (instead of changing attributes).

As I'm trying to find out a better solution (maybe there is another, which I don't know about) and get good manners in programming applications. What do you think, what is better solution (maybe in one scenario and in other scenario another solution)? To maintan duplicate code or complex code (maybe from real experience)?

  • What is the purpose of the single-parameter constructor? – Robert Harvey May 13 '16 at 20:58
  • For example: you want to store some data to file and you have entropy (byte array) used to encrypt this data. Or maybe service with som information needed for login or communication with it (security string, ...). Is that, what are you asking? – joettriscik May 13 '16 at 21:17
3

I would just treat the classes you're going to serialize and deserialize as DTO's, and choose which utility method you wish to perform the serialization/deserialization. That way you only need a single class to carry the information.

Each utility method can contain some parameter validation, if you're worried about a bad parameter. If you want to keep the methods in the DTO, create a couple of factory methods that accept a serialization data source and return a DTO.

The one thing I would not do is have two model classes just because you want to automagically new up one, but manually new up the other with an enforced constructor. That creates two different places for your data, and violates the Single Source of Truth principle.

Keep it simple, soldier.

2

I find it quite annoying that many serialization libraries require parameterless constructors.

With the ability to annotate parameters, one could in theory specify associations between constructor parameters and serialization elements, so that the serialization library can collect all the necessary elements and pass them to the constructor of the deserialized object. Alas, I have yet to come across a serialization library that offers such a feature.

Furthermore, the ability to serialize objects with parametrized constructors is necessary even if we are not planning to parametrize them with deserialized values; for example, in order to implement Dependency Injection without resorting to magic like Spring.NET.

As far as I know, the way most people handle this is by having two separate sets of classes, one for the application logic to work with, and one for serialization only.

It turns out that this is not too bad, because the data model that you want to work with in your application logic is usually not the exact same as the data model that makes sense to persist.

For example, you may want to have the freedom to change your application data model while maintaining compatibility with serialization files that were created from older versions of your data model. If one set of classes is all you have, then the moment you make a change to your data model, all of your old data are suddenly unreadable. If you maintain your application data model separately from your serialization data model, then you can have different (concurrently existing) versions of your serialization data model for use with different versions of existing serialization files.

  • 3
    Maintaining two separate sets of classes just because serialization libraries require parameterless constructors is one of those situations where the cost exceeds the benefits. – Robert Harvey May 13 '16 at 23:34
  • @RobertHarvey he makes a good point about backward compatibility with old files. I've run into that situation before myself. Of course, I don't think I would introduce a second set of classes until it was actually a problem to be solved though. – RubberDuck May 14 '16 at 15:09
  • I find it quite understandable that many serialization libraries require parameterless constructors, because it helps to keep the serialization mechanics much simpler and cleaner- – Doc Brown May 14 '16 at 15:12
  • @RobertHarvey I read your answer. I agree that it would be a bad idea to have a separate persistence data model just because of parameterless constructors. But I think my answer is making a much stronger point. – Mike Nakis May 14 '16 at 19:48
  • @DocBrown I would think that the purpose of a library is primarily to make things simpler and cleaner for consumers of the library, not necessarily for the library itself. – Mike Nakis May 14 '16 at 19:50
2

Keep this pragmatic - creating a lot of duplicate or boilerplate code should be a no-go, as well as reimplementing a mature serialization mechanism of the framework by yourself. So bite the bullet and provide a parameterless constructor - this, in reality, does not turn out to be so bad as you might think.

You can make the constructor private and provide a static method inside of the class for deserialization, which makes sure the "mandatory constructor attribute" is passed into the object after deserialization. That makes it at least unlikely someone will forget to set the additional attribute.

  • Is "byte the bullet" some pun I'm not getting or just a typo? – svick May 14 '16 at 0:51
  • "Bite the bullet" is an idiom meaning to endure something painful or difficult: english.stackexchange.com/q/247999/151980. And "byte" in this referring to enduring something painful related to computers :) – Greg Burghardt May 14 '16 at 14:52
  • Hey guys, it was just a typo, and I corrected it already. – Doc Brown May 14 '16 at 15:10
  • Doc, most of the serialization library's I've worked with require a public parameterless ctor. – RubberDuck May 14 '16 at 15:10
  • I actually liked the unintentional pun... – RubberDuck May 14 '16 at 15:11
1

What about two static methods in the class for seralize and de serailize. The parameter is a argument of this funuction and is stored as a constant somewhere.

If you end up having lots of these objects, make a class which has a list of these. This should have the ability to relate the methods on the whole list. It would also take a filter delegate and give a new list after filtering (don't delete data before saving). It would also the two static methods. The class and the one other parameter are arguments.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.