Sign in to follow this  

[.net] Abandoning .net serialization?

This topic is 3616 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

A little while ago I got into a fight with .NET serialization and wound up abandoning it in favor of writing my own methods. The fact that it's a complete black box is one issue, as it makes finding serialization errors very tedious. The most important reason I chose not to use it is because of how it handles complex objects which implement ISerializable, holding other objects which also implement ISerializable. When evaluating the name/value pairs given to it by the serialization methods, it actually evaluates all of the pairs of the initial class, and THEN goes and evaluates the name/value pairs of the contained objects. Sorry if this doesn't make sense, in other words deserialization goes breadth first instead of depth first. The result of this is that deserialization code isn't run in the same order as it is written, which resulted in a lot of subtle problems. Interestingly, this doesn't happen if the class is simply marked as Serializable and doesn't implement ISerializable, but that isn't an option in my case. I might have been able to get around it using some callbacks or breaking the deserialization into parts or something, but I didn't want to bring that kind of complexity into my classes. What's done is done and it would take too much effort to go back to serialization, but there's always been this nagging suspicion that there's just one small bit of information that would have instantly solved my problem. "Oh, you just need to put the [Fnord] tag on your objects, then they will magically work the way you want!" Unfortunately all the material I can find on .net serialization is just basic examples of how to save a simple object, and nothing touches on the point I'm making here.

Share this post


Link to post
Share on other sites
Perhaps a little more of the context where you are using might help. I haven't directly dealt with serialization myself, but if we can know exactly what you are doing someone here who has experience might be able to lend a hand. Either that, or post a few source snippets.

Share this post


Link to post
Share on other sites
Hombert,

As of .Net 1.1 the ISerializable was the prefered way to handle complex serialization. When .Net 2.0 came out the suggested method moved to attribute based methods instead. The following shows some basics that I use (this is an example and excludes much of the actual class but the serialization basics):


[Serializable()]
public class ModelVertexMap
{

#region Variables

internal TextureMapProxy tmapProxy;
[NonSerialized()]
internal TextureMap tmap;

#endregion

#region Constructors / Destructor

/// <summary>
/// Used for serialization.
/// </summary>
public ModelVertexMap()
{
}

#endregion

#region Serialization/Deserialization

/// <summary>
/// Fires after de-serialization has occured.
/// </summary>
/// <param name="context">Any context data sent to the object being de-serialized.</param>
[OnDeserialized()]
private void Deserialize(System.Runtime.Serialization.StreamingContext context)
{
this.deserializing = true;
Platform.PlatformContext pc = context.Context as Platform.PlatformContext;
if (pc != null)
{
if (this.tmapProxy != null)
{
this.TextureMap = (pc.Platform as IModelEngine).TextureEngines.FindVertexMap(this.tmapProxy);
}
}
else throw new SerializationException("Invalid context object PlatformContext in VertexMap, Context object cannot be null!");
this.deserializing = false;
}

/// <summary>
/// Fires before serialization occures.
/// </summary>
/// <param name="context">Any context data that needs to be sent to the object being serialized.</param>
[OnSerializing()]
private void Serialize(System.Runtime.Serialization.StreamingContext context)
{
/*Serialize a proxy for the texture map only since the maps and engines can be arranged in
* a different sequence later on when this object is loaded again. The TextureMap's
* name and Engine name will need to be used to find the TextureMap assigned to it
* currently. Thus the need for a proxy that stores only this information.
*/

if (this.tmap != null) this.tmapProxy = new TextureMapProxy(this.tmap);
}

#endregion

}




As you can see you can mark methods with attributes that are called during the serialization process that are supplied with the context and serialization data. You can supply your own custom context objects that are needed during the serialization process as well.

Check out the attributes in the Serialization namespace there are ones that fire at various points during the process.

HTH,

-Devin

Share this post


Link to post
Share on other sites
The next time I make a project I think I'll go that route. The problem is that all the resources I've found on Serialization never mentioned that methods can be tagged. That isn't something new, is it? Everything I see just suggests using ISerializable.

My own serialization process isn't so bad, it uses .net serialization for the parts that it can work for and its less work to serialize an object at the class level (at the expense of having to write my own serialization engine but that wasn't so bad). In the future I'll go the attribute route though.

Share this post


Link to post
Share on other sites
I haven't worked TOO much with .NET serialization so far, but it does seem pretty nice. It does take some wading through the options (want XML or binary output? implement ISerializable or use attributes?) to figure out the technique.

That said, I have been using the attributes technique to serialize an object graph to XML and bring it back again. Outside of the classes involved, it just takes a few lines of code to kick off the de/serialization process.

True, you could write your own de/serialization functionality, but why not use what's in place already? I used to do the above work in the old way, manually reading and writing XML files, but when I discovered /NET serialization I was able to eliminate quite a lot of extraneous code.

Share this post


Link to post
Share on other sites
Quote:

True, you could write your own de/serialization functionality, but why not use what's in place already?

Well, there are occasionally valid reasons -- I manually(*) serialize my asset classes, for example, because the .NET serialization is quite verbose and not exactly easy to restore manually (e.g., from native C++ code).

(*) Rather, of course, my asset tool generates me code to serialize them manually.

Share this post


Link to post
Share on other sites
I've written my own code for it, which serializes it to a REALLY ugly XML format. All my classes implents ISavable, which has the methods required. If I try to serialize an object which doesn't implent ISavable, it instead uses BinaryFormatter for that object, and saves it to the XML file with base64 encoding.

The reason for this is that BinaryFormatter sometimes have problems with objects that change from serialization to deserialization. This happens quite often in my development process. Implenting ISerializable didn't help.

Share this post


Link to post
Share on other sites
Quote:
Original post by HombertThe problem is that all the resources I've found on Serialization never mentioned that methods can be tagged.


You mean fields instead of methods? Fields can be tagged as non-serialized.

There is some weird decisions made in the serialization classes provided. The XML serializer will only serialize public read/write properties (and maybe fields?), so if you have private data you want serialized, that's out unless you want implement IXmlSerializable and do your own XML parsing. Then there's the Soap formatter, which gives nice XML output, but fails if its asked to serialize any generic container. I'd really like to have a formatter that will serialize my objects to human-readable XML, and can handle generic containers and deal with private fields.

I've often wondered how much work it would be to implement the IFormatter interface myself to write a formatter that does what I want. But I've never tried or done any research on how to do it. But if you don't like the way BinaryFormatter works, you might consider using this as an option instead of throwing away everything in the System.Runtime.Serialization namespace. If you ever do something like that, I'd be interested to hear about it.

Share this post


Link to post
Share on other sites
Quote:
Original post by kanato
There is some weird decisions made in the serialization classes provided. The XML serializer will only serialize public read/write properties (and maybe fields?), so if you have private data you want serialized, that's out unless you want implement IXmlSerializable and do your own XML parsing. Then there's the Soap formatter, which gives nice XML output, but fails if its asked to serialize any generic container. I'd really like to have a formatter that will serialize my objects to human-readable XML, and can handle generic containers and deal with private fields.
Windows Communication Foundation can serialize public/private fields and properties, as well as generic types. It also supports serialization of types marked with the Serializable attribute. See this page for more information.

Of course, it's yet another serialization library to learn and requires .NET 3.0, but I've found it to be pretty easy so far. Here's a simple example:

[DataContract] // Indicates a type serializable by DataContractSerializer.
class Person
{
[DataMember]
public Name { get { return _name; } set { _name = value; } }

[DataMember]
private int _id;

[DataMember]
private List<Pet> Pets;

[DataMember]
private Uri _uri; // Uri has Serializable attribute

private string _name;
}

DataContractSerializer serializer = new DataContractSerializer(typeof(Person));

Stream stream;
serializer.WriteObject(stream, new Person());

Person p = (Person)serializer.ReadObject(stream);

Share this post


Link to post
Share on other sites
Quote:
Original post by DvDmanDT
Search for XmlFormatter. I think it's pretty good, but missing something making it unusable for me. Perhaps it can be extended to handle it though?


Do you mean this one? I was playing with it, but it doesn't seem to do generic lists or dictionaries. There seem to be some bugs; the example there breaks if you remove the ISerializable implementation from their "MyObject" class, even though the object does nothing that the Binary or Soap formatters shouldn't be able to handle without implementing ISerializable. It seems like it could be a good starting point for developing something that could do what I want though.

The WCF method looks promising, but it's not supported under Mono at the moment, so it's not really useful to me. But I think they intend to support it at some point, so it may be a useful option in the future.

Share this post


Link to post
Share on other sites
Quote:
Original post by jpetrie
Quote:

True, you could write your own de/serialization functionality, but why not use what's in place already?

Well, there are occasionally valid reasons -- I manually(*) serialize my asset classes, for example, because the .NET serialization is quite verbose and not exactly easy to restore manually (e.g., from native C++ code).

(*) Rather, of course, my asset tool generates me code to serialize them manually.


Fair enough. Edge cases to tend to pop up from time to time, and they have to be dealt with, one way or another. As mentioned previously, I haven't too much experience with the XML Serializer yet, but it works pretty well for me, and I was too happy to switch from the manual technique.

Share this post


Link to post
Share on other sites
Quote:
The problem is that all the resources I've found on Serialization never mentioned that methods can be tagged.


I bought a great book called "Pro C# 2005 and the .NET 2.0 Platform" some time ago and it's been a super easy and informative book. Explains all about the serialization and attributes. Also about the ISerializable interface as well. It's by Andrew Troelson. I'm sure they have a new version out by now.

Still, if you have VS2005 or later and have the help installed it should all be there. If you go to the VS2005 help (I think it's the msdn docs) and index one of the attributes and go to the about portion it should give info needed to use that attribute...

[the index from help]

OnDeserialized class
about OnDeserializedAttribute class
all members
...

[click on "about OnDeserializedAttribute class" to bring up super help...]


using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

public class Test
{
public static void Main()
{
// Create a new TestSimpleObject object.
TestSimpleObject obj = new TestSimpleObject();

Console.WriteLine("\n Before serialization the object contains: ");
obj.Print();

// Open a file and serialize the object into binary format.
Stream stream = File.Open("DataFile.dat", FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();

try
{
formatter.Serialize(stream, obj);

// Print the object again to see the effect of the
//OnSerializedAttribute.
Console.WriteLine("\n After serialization the object contains: ");
obj.Print();

// Set the original variable to null.
obj = null;
stream.Close();

// Open the file "data.xml" and deserialize the object from it.
stream = File.Open("DataFile.dat", FileMode.Open);

// Deserialize the object from the data file.
obj = (TestSimpleObject)formatter.Deserialize(stream);

Console.WriteLine("\n After deserialization the object contains: ");
obj.Print();
}
catch (SerializationException se)
{
Console.WriteLine("Failed to serialize. Reason: " + se.Message);
throw;
}
catch (Exception exc)
{
Console.WriteLine("An exception occurred. Reason: " + exc.Message);
throw;
}
finally
{
stream.Close();
obj = null;
formatter = null;
}

}
}


// This is the object that will be serialized and deserialized.
[Serializable()]
public class TestSimpleObject
{
// This member is serialized and deserialized with no change.
public int member1;

// The value of this field is set and reset during and
// after serialization.
private string member2;

// This field is not serialized. The OnDeserializedAttribute
// is used to set the member value after serialization.
[NonSerialized()]
public string member3;

// This field is set to null, but populated after deserialization.
private string member4;

// Constructor for the class.
public TestSimpleObject()
{
member1 = 11;
member2 = "Hello World!";
member3 = "This is a nonserialized value";
member4 = null;
}

public void Print()
{
Console.WriteLine("member1 = '{0}'", member1);
Console.WriteLine("member2 = '{0}'", member2);
Console.WriteLine("member3 = '{0}'", member3);
Console.WriteLine("member4 = '{0}'", member4);
}

[OnSerializing()]
internal void OnSerializingMethod(StreamingContext context)
{
member2 = "This value went into the data file during serialization.";
}

[OnSerialized()]
internal void OnSerializedMethod(StreamingContext context)
{
member2 = "This value was reset after serialization.";
}

[OnDeserializing()]
internal void OnDeserializingMethod(StreamingContext context)
{
member3 = "This value was set during deserialization";
}

[OnDeserialized()]
internal void OnDeserializedMethod(StreamingContext context)
{
member4 = "This value was set after deserialization.";
}
}

// Output:
// Before serialization the object contains:
// member1 = '11'
// member2 = 'Hello World!'
// member3 = 'This is a nonserialized value'
// member4 = ''
//
// After serialization the object contains:
// member1 = '11'
// member2 = 'This value was reset after serialization.'
// member3 = 'This is a nonserialized value'
// member4 = ''
//
// After deserialization the object contains:
// member1 = '11'
// member2 = 'This value went into the data file during serialization.'
// member3 = 'This value was set during deserialization'
// member4 = 'This value was set after deserialization.'





HTH,

Devin

Share this post


Link to post
Share on other sites
Quote:
Original post by DvDmanDT
The reason for this is that BinaryFormatter sometimes have problems with objects that change from serialization to deserialization. This happens quite often in my development process. Implenting ISerializable didn't help.


It is to be expected that binary serialization breaks if you modify the object being serialized/deserialized. Binary serialization can be thought of as taking the object and flattening it into a bytestream. There's no cataloguing or marking of individual pieces of data as with the XML serializer. If the object that we're attempting to load the serialized data into doesn't match exactly the runtime cannot deal with it -- type safety is extremely important in .NET. (Reminds me of the old EnvyMUD days when I'd use the first byte of a memory structure to determine what type of object it was... neat trick, but ultimately not safe.)

This lack of individual data separation is one of the reasons why the binary serializer beats the pants off the XML serializer in terms of data saved/memory used for serialization. The XML serializer uses "<RandomInt>4</RandomInt>" while the binary serializer uses "0004".

The XML formatter works in the opposite manner, which is why it only serializes public fields/properties. If a property doesn't exist, the XML formatter continues without complaining.

With ISerializable it IS noted that there is no guarantee as to the order of recreation. (I think this has more to do with the SerializationInfo object being parsed completely by the constructor before moving on to the next object in the graph than anything else.)

Can you not handle what you need to using the OnSerializing, OnSerialized, OnDeserializing, and OnDeserialized attributes? When one of the objects/properties/fields you're having problems with is either being prepared to be deserialized or has been, take whatever steps are necessary? Or is that your reference to breaking into parts/complexity?

With that said, custom serialization isn't bad. Especially if you're more concerned with storing data in a file rather than sending the object across remoting boundaries or app domains. I'm not a big fan of declarative programming (attribute based decision making) as it tends to hide itself and how it works.

Share this post


Link to post
Share on other sites
Sorry for the slightly off-topic question: if two classes have the same fields, but they're defined in different, non-accessible assemblies, would deserialization still be possible?

I am currently porting some code from VB.NET to C#, and I cannot reference the original assembly. I'd rather use serialization instead of reading bytes out of a file - which gets boring especially when dealing with arrays I guess.

Share this post


Link to post
Share on other sites
Quote:
Original post by Rainweaver
Sorry for the slightly off-topic question: if two classes have the same fields, but they're defined in different, non-accessible assemblies, would deserialization still be possible?

I am currently porting some code from VB.NET to C#, and I cannot reference the original assembly. I'd rather use serialization instead of reading bytes out of a file - which gets boring especially when dealing with arrays I guess.
I'm not sure about .NET serialization (I would guess that this is not possible), but WCF's DataContractSerializer was built to handle this scenario.

Share this post


Link to post
Share on other sites
Quote:
Original post by Rainweaver
Sorry for the slightly off-topic question: if two classes have the same fields, but they're defined in different, non-accessible assemblies, would deserialization still be possible?

I am currently porting some code from VB.NET to C#, and I cannot reference the original assembly. I'd rather use serialization instead of reading bytes out of a file - which gets boring especially when dealing with arrays I guess.


XML serialization would handle this situation as well.

Share this post


Link to post
Share on other sites
I have just discovered serialization and I have to ask what's the point of it? Both binary and XML serialization end up with bigger files then if I just store my data in a text file and I dont see any speed advantage at all except maybe with loading but certainly not with saving.

Share this post


Link to post
Share on other sites
Quote:
Original post by gp_s
I have just discovered serialization and I have to ask what's the point of it? Both binary and XML serialization end up with bigger files then if I just store my data in a text file and I dont see any speed advantage at all except maybe with loading but certainly not with saving.
Serialization can be complex and error-prone to write. It's generally easier to do something like WriteObject(stream, stuffToSerialize) than to write a dozen methods to serialize your data.

With XML serialization you also have the benefit of a standard format.

Share this post


Link to post
Share on other sites
Quote:
Original post by gp_s
I have just discovered serialization and I have to ask what's the point of it? Both binary and XML serialization end up with bigger files then if I just store my data in a text file and I dont see any speed advantage at all except maybe with loading but certainly not with saving.

Because, it's pre-rolled, meaning it's a little more robust than anything you could whip together in a few hours, and in the case of XML serialization, it saves the structure of the object as well, which helps when consuming the objects across platforms.

Share this post


Link to post
Share on other sites

This topic is 3616 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this