views:

639

answers:

7

I need to save some classes and data structures to a file. My first reflex was to use XML or Binary serialization but this is turning into a nightmare. I have a set of classes that were not meant to be serialize (private setters, no parameterless constructors, no Serialization attribute, dictionaries, etc.). Considering that I cannot change those classes, what should I do? Is there any workaround this and still use serialization?

Am I going to have to write all the code to write the properties, collections, etc?

A: 

I would use a code gen tool (mygeneration, T4, whatever) to generate the DTOs to serialise...

Ali Shafai
The problem with code genning your way out of this is that if the class changes you have to code gen again. This may or may not be a problem.
Sam Saffron
I dont see any reason to -1 this ...
Sam Saffron
A: 

Not a simple workaround.

You probably only want to serialize/deserialize the fields (private and public) in the classes, and do that recursively. You will probably need to use reflection to get at them. Don't de/serialize properties, because there could be side-effects when you set a property value. You'll also have to recursively de/serialize all the fields which are Objects. The challenge is in knowing where to stop.

You'll have to make some big guesses about how to deserialize a serialized object using reflection. I have to assume that some of the fields of those objects contain references to other objects, and you'd have to know how to traverse all that out as well.

Additionally, you'll have to call the parameterized constructors and hope that you're not doing too much damage when setting fields with reflection.

In short, you might be better off creating a special-purpose set of wrapper classes which does a best-guess form of re-creating your proprietary classes from a serialized format. Your special-purpose classes could then be made to be serializable. This is not going to be a simple task.

That said, code gen might be able to simplify the task of identifying all the fields in the classes.

Alan McBee
A: 

Another option is to use an adapter pattern.

Good news is that you won't have to change the original class. Bad news is that you'll probably end up writing as twice as much code as exists in the first "unchangeable" class.

You'll end up with 2 new classes: the adapter, and the new serializble class.

The idea being that the adapter knows how to create the serizliable class by examining the unserializable class. To go the other way (serializable to unserializable) you can again use an adapter (of course, here I am assuming that your private setters are actually set via the parameterized constructor).

Here's the pattern in detail.

corey broderick
A: 

There are two separate parts to this:

  1. extracting/setting the data
  2. storing/reading it from file (or other location)

For the first part, if you cannot change the classes you want to serialize/deserialize, the you are really just left with doing a deep copy through reflection.

For serialization, go through each member of the class storing its value.

Use which ever medium you want to store it in, for instance XML, and write these values to disk.

For deserialization, read in the values from the file, create a new instance of the object and use reflection to set all the values.

If you get/set all the members you will have an object that is in an identical state. (You may need to recursively go through all your objects if you have complex members.)

As for how you store the data on disk, xml or binary both work. If you want to see it and have it be human readable, then go with XML. (I'd recommend this for your initial stab at it since it will make debugging much easier.)

GreenKiwi
+5  A: 

Sounds like a job for... serialization surrogates!

Go to http://msdn.microsoft.com/en-us/magazine/cc188950.aspx

for an overview.

Sam
+1: Surrogates will play nice with BinaryFormatter.
RaphaelSP
A: 

It really depends on the scale of your problem and the performance you need.

If you have 1 problem class, I'd just write a surrogate class that can serialize/deserialize that class. If you are talking about 100s of classes you probably need to be using a serialization framework that supports all the intricacies. No parameterless construction is a nightmare, whatever serialization framework you write or use you will need to know up-front the params to pass to the constructor. Private setters / dictionaries / lists etc are not too much of a problem.

I wrote a mini-serialization framework for Media Browser which is BSD. My focus was on performance, but the techniques I use can be adapted to your problem. Similarly techniques used in Marc's protocol buffers could be used.

If performance is not important at all, a fairly trivial serialization framework can be coded fairly quickly that uses reflection, stuff gets hard when you try to keep the thing performant.

Sam Saffron
+5  A: 

Use the JavaScriptSerializer. It's in the System.Web.Script.Serialization namespace and is implemented in the 3.5 framework in the System.Web.Extensions.dll assembly.

With this class, you can serialize any POCO whether it's marked as [Serializable] or not. Your program does not have to be a web application to use JSON serialization. Here's an example:

public class Unserializable
{
  public int Age { get; set; }
  public int ID { get; set; }
  public string Name { get; set; }
}

public class Program
{
  static void Main()
  {
    var u = new Unserializable
            {
              Age = 40,
              ID = 2,
              Name = "Betty"
            };
    var jser = new JavaScriptSerializer();
    var jsonText = jser.Serialize( u );
    // next line outputs {"Age":40,"ID":2,"Name":"Betty"}
    Console.WriteLine( jsonText );
  }
}
W. Kevin Hazzard
+1 for I did not know that. Does this handle fields too?
Alex James
Yes, it handles fields, too. Try this:public class Unserializable { public int Age; public int ID { get; set; } public string Name { get; set; } public Unserializable(int age, int id, string name) { Age = age; ID = id; Name = name; }}var u = new Unserializable( 40, 2, "Donna" );var jser = new JavaScriptSerializer();var jsonText = jser.Serialize(u);Console.WriteLine("Serialized {0} using " + "JavaScriptSerializer into {1}:", u.GetType().Name, jsonText);This outputs JSON text for the object containing the public field as '{"Age":40,"ID":2,"Name":"Donna"}'.
W. Kevin Hazzard