Ben McIntosh
Ben McIntosh

Reputation: 1552

Is there a way to mark a class field to only be de-serialized and not serialized?

It sounds strange but this is exactly what I want because I am using a data structure named "Project" which gets serialized to a save file. I would like to be able to de-serialize an older version of a save file with deprecated fields in tact, but then re-serialize it using only the currently used fields. The problem is that I want to get rid of those deprecated fields when re-serializing the structure in order to minimize file size. Is it possible to mark a field as "de-serializable only"?

Edit:

Thanks for the ideas! I decided to build mostly off of NickLarsen's suggestions and create an old version of the project structure with all depreciated fields in a separate namespace. The difference is that I decided to perform the upgrade upon deserialization. This is great because I can do it all in one line (hopefully you can get the gist what I'm doing here):

Project myProject = new Project((Depreciated.Project)myFormatter.Deserialize(myStream));

The constructor simply returns a new instance of the fresh minimal data structure based on the old bloated one.

Second Edit:

I decided to follow the advice of bebop instead and create new classes for each project version with the oldest version including all depreciated and new fields. Then the constructor of each project upgrades to the next version getting rid of depreciated fields along the way. Here is an illustrative example of converting from version 1.0.0 -> 1.0.5 -> current.

Project myProject = new Project(new Project105((Project100)myFormatter.Deserialize(myStream)));

One key to this is to forced the deserialized file as well as any fields into the older versions of the classes by using a SerializationBinder.

Upvotes: 2

Views: 545

Answers (5)

Sam Holder
Sam Holder

Reputation: 32936

could you not create a new version of your data structure class each time the structure changes, and have the constructor for the new class take an instance of the previous class, and populate itself from there. To load the newest class you try and create the earliest class from the serialised file until one succeeds, and then pass that into the constructor of the next class in the chain repeatedly until you get the latest version of the data structure then you can save that.

Having a new class for each change in format would avoid having to change any existing code when the data structure changed, and your app could be ignorant of the fact that the save file was some older version. It would allow you to load from any previous version, not just the last one.

This sort of thing implemented by a chain of responsibility can make it easy to slot in a new format with minimal changes to your existing code.

Whilst not a textbook chain of responsibility you could implement with something like this:

(NOTE: untested code)

public interface IProductFactory<T> where T : class
{
    T CreateProduct(string filename);
    T DeserializeInstance(string filename);
}

public abstract  class ProductFactoryBase<T> :  IProductFactory<T> where T : class
{
    public abstract T CreateProduct(string filename);

    public T DeserializeInstance(string filename)
    {
        var myFormatter = new BinaryFormatter();
        using (FileStream stream = File.Open(filename, FileMode.Open))
        {
            return myFormatter.Deserialize(stream) as T;
        }

    }
}

public class ProductV1Factory : ProductFactoryBase<ProductV1>
{
    public override ProductV1 CreateProduct(string filename)
    {
        return DeserializeInstance(filename);
    }
}

public class ProductV2Factory : ProductFactoryBase<ProductV2>
{
    ProductV1Factory successor = new ProductV1Factory();
    public override ProductV2 CreateProduct(string filename)
    {
        var product = DeserializeInstance(filename);
        if (product==null)
        {
            product = new ProductV2(successor.CreateProduct(filename));
        }
        return product;
    }

}

public class ProductV2
{
    public ProductV2(ProductV1 product)
    {
        //construct from V1 information
    }
}


public class ProductV1  
{
}

this has the advantage that when you want to add ProductV3 you only need to change the class you are using in your app to be a ProductV3 type, which you need to do anyway, then you change your loading code so that it uses a ProductV3Factory, which is basically the same as a ProductV2Factory, but it uses a ProductV2Factory as the successor. You don't need to change any existing classes. you could probably refactor this a bit to get the commanality of CreateProduct into a base class, but it gets the idea across.

Upvotes: 1

Crippledsmurf
Crippledsmurf

Reputation: 4012

There isn't an attribute defined to explicitly support that but one possbility may be to define custom serialization using either ISerializable or by defining a custom serializer class for your type that takes no action when serializing

Upvotes: 0

Ucodia
Ucodia

Reputation: 7710

I think what you are searching for is the OptionalFieldAttribute.

Upvotes: 0

Nick Larsen
Nick Larsen

Reputation: 18877

You have a couple options for this.

First you could create a version of your class (maybe in a different namespace) for the old format, and one for the new format. In the old class, overload the serialize function to throw an error, or convert itself to the new class and serialize that.

Second, you could just write your own serializer, which would be a bit more involved. There are plenty of resources which can help you though.

Upvotes: 1

Pike65
Pike65

Reputation: 572

As far as I know .NET is very careful to only serialise things it can deserialise and vice versa.

Upvotes: 0

Related Questions